diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..81154dd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+samples
diff --git a/api/plan-from-log.xqy b/api/plan-from-log.xqy
new file mode 100644
index 0000000..81d584e
--- /dev/null
+++ b/api/plan-from-log.xqy
@@ -0,0 +1,24 @@
+import module namespace qputils = "http://marklogic.com/optic/qputils" at "../lib/qputils.xqy";
+
+xdmp:set-response-content-type("application/json"),
+let $file := xdmp:get-request-field("file")
+let $id := xdmp:get-request-field("id")
+let $type := xdmp:get-request-field("type", "estimate")
+
+
+let $id := "sessionKey=" || $id || " "
+
+let $regex := switch ($type)
+ case "estimate" return ("Event:id=Optic Plan Trace" || ".*" || $id || "plan=")
+ case "execution" return ("Event:id=Optic Execution Diagnostics Trace" || ".*" || $id )
+ case "optimization" return ("Optic Optimization Trace"|| ".*" || $id )
+ default return fn:error("QV-ARG", "Invalid type")
+
+
+let $res := xdmp:logfile-scan($file, $regex)
+let $res := if ($type = ("optimization"))
+ then qputils:parseOptimization ($res ! fn:substring-after(., $id))
+ else qputils:makeGraph(xdmp:unquote(fn:substring-after($res, "
"))/*,"N")
+
+
+return json:to-array($res)
\ No newline at end of file
diff --git a/api/scan-for-plans.xqy b/api/scan-for-plans.xqy
new file mode 100644
index 0000000..d2e7a12
--- /dev/null
+++ b/api/scan-for-plans.xqy
@@ -0,0 +1,35 @@
+
+xdmp:set-response-content-type("application/json"),
+
+array-node {
+ let $file := xdmp:get-request-field("file")
+ let $trace := xdmp:get-request-field("trace")
+ let $regex := xdmp:get-request-field("regex", "( plan=| diagnostic_plan=)")
+ let $regex :=
+ if ($trace)
+ then ("trace=" || $trace || ".*" || $regex )
+ else $regex
+ let $start := xdmp:get-request-field("start")
+ let $start :=
+ if ($start)
+ then xs:dateTime (fn:substring(xdmp:logfile-scan($file, (), (), (), (), 1),1,10) || "T" || $start)
+ else ()
+ let $end := ()
+ let $limit := 20
+ for $e in xdmp:logfile-scan($file, $regex, "s", $start, $end, $limit)
+ let $type := if (fn:contains($e, "diagnostic_plan")) then "execution" else "estimate"
+ let $trace :=
+ if (fn:contains($e, "trace="))
+ then fn:substring-before(fn:substring-after($e, " trace="), " sessionKey=")
+ else ""
+ let $key :=
+ if ($type eq "estimate")
+ then fn:substring-before(fn:substring-after($e, " sessionKey="), " plan=")
+ else fn:substring-before(fn:substring-after($e, " sessionKey="), " diagnostic_plan=")
+ let $time := fn:substring($e, 1, 23)
+ return
+ object-node { "time" : $time, "key" : $key, "trace": $trace }
+
+}
+
+
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..a962e04
Binary files /dev/null and b/favicon.ico differ
diff --git a/lib/qputils.xqy b/lib/qputils.xqy
index 4adc6a5..d1a8b9d 100644
--- a/lib/qputils.xqy
+++ b/lib/qputils.xqy
@@ -866,3 +866,58 @@ declare function makeScripts($in as element())
)
};
+
+
+declare function qputils:normalize ($string) {
+ let $res := fn:normalize-space($string)
+ return
+ if (fn:starts-with($res, "("))
+ then fn:substring($res, 2, fn:string-length($string) -2)
+ else $res
+};
+
+declare function qputils:parseCost ($line, $obj) {
+ let $line := qputils:normalize ($line)
+ let $cost := fn:substring-before($line,"crd:")
+ let $card := fn:substring-before(fn:substring-after($line,"crd:["),"]")
+ let $_ := map:put($obj, "cardinalities", $card)
+ let $_ :=
+ for $token in fn:tokenize($cost,",")
+ let $t := fn:tokenize($token, ":")
+ let $name := $t[1]
+ let $name :=
+ if ($name = ("mem","dmem","nw","io"))
+ then ($name|| "-cost")
+ else if (fn:starts-with($name,"dcpu")) then "dcpu-cost"
+ else if (fn:starts-with($name,"cpu")) then "cpu-cost"
+ else if ($name = "m") then "cost"
+ else if ($name = "c") then "estimated-count"
+ else if ($name = "r") then "rule-count"
+ else $name
+ return map:put($obj,$name,qputils:normalize($t[2]))
+ return ()
+};
+
+declare function qputils:parseOptimization ($lines) {
+ for $line at $j in $lines
+ let $tokens := fn:tokenize($line, " ")
+ let $name := if (fn:contains($line, "initialCost")) then "start"
+ else if (fn:contains($line, "bestCost")) then "end"
+ else "iteration " || ( $j - 1)
+ let $obj := map:new() => map:with("_id", "node_" || $j) => map:with("_name", $name)
+ let $_ := if ($j gt 1) then map:put($obj,"_parent", "node_" || $j - 1) else ()
+
+ let $res :=
+ for $token in $tokens
+ let $t := fn:tokenize($token, "=")
+ let $tagname :=
+ switch ($t[1])
+ case "t" return "temperature"
+ case "r" return "repeats"
+ case "c" return "cool"
+ default return $t[1]
+
+ where fn:matches($name, "^\w")
+ return if (fn:starts-with($t[2], "(")) then qputils:parseCost($t[2], $obj) else map:put($obj,$tagname,$t[2])
+ return ($obj)
+};
diff --git a/log.html b/log.html
new file mode 100644
index 0000000..d94c86a
--- /dev/null
+++ b/log.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+
-
+
+
+
+
+
};
-let $in := xdmp:unquote (xdmp:get-request-field("plan") )/*
+
+let $file := xdmp:get-request-field("filename")
+let $plan := xdmp:get-request-field("plan")
+
+let $in := if ($file) then xdmp:document-get($file)/*
+ else if ($plan) then xdmp:unquote (xdmp:get-request-field("plan"))/*
+ else fn:error(xs:QName("XDMP-ARG"), "Missing argument: filename or plan required")
return (
xdmp:set-response-content-type("text/html"),
diff --git a/ui/css/style.css b/ui/css/style.css
index bcb25d7..f36f1da 100644
--- a/ui/css/style.css
+++ b/ui/css/style.css
@@ -46,15 +46,15 @@
margin-left: 2px;
text-align: left;
color: #505050;
+ line-height: 1.0;
}
-#viewer {
- overflow: "auto";
-}
+
body {
- background: #F8F8F8;
- font-family: Arial, Helvetica, sans-serif;
+ padding: 0;
+ margin: 0
+ height: 100%;
}
.tooltip {
@@ -73,7 +73,87 @@ body {
z-index: 5
}
+.tooltip table {
+ border-collapse: collapse;
+ white-space: pre-wrap;
+ font-size: 1em;
+}
+.tooltip table > tr > td {
+ border: 1px solid goldenrod;
+}
+.tooltip table > tr:first-child > td {
+ border-top: 0;
+}
+.tooltip table > tr > td:first-child {
+ border-left: 0;
+}
+.tooltip table > tr:last-child > td {
+ border-bottom: 0;
+}
+.tooltip table > tr > td:last-child {
+ border-right: 0;
+}
+
pre {
white-space: pre-wrap;
font-size: .80em;
}
+
+
+table.plantable {
+ background-color: #FFFFFF;
+ border-collapse: collapse;
+ border-width: 1px;
+ border-color: steelblue;
+ border-style: solid;
+ color: #000000;
+}
+
+table.plantable td, table.plantable th {
+ border-width: 1px;
+ border-color: steelblue;
+ border-style: solid;
+ padding: 5px;
+}
+
+table.plantable thead {
+ background-color: whitesmoke;
+}
+
+
+
+table.criteria td, table.criteria th {
+ padding: 5px;
+}
+
+.full-height {
+ height: 100%;
+}
+
+.qv-tabs {
+ height: 34px;
+}
+.qv-banner {
+ height: 100%;
+}
+
+.qv-vertical-center {
+ margin: 0;
+ position: absolute;
+ top: 70px;
+
+}
+
+
+#viewer {
+ overflow: auto;
+ max-width: 100%;
+ height: 100%;
+}
+
+#wrapper {
+width: 100%;
+margin: 0;
+background: #F8F8F8;
+}
+
diff --git a/ui/css/w3-theme.css b/ui/css/w3-theme.css
new file mode 100644
index 0000000..cb5b884
--- /dev/null
+++ b/ui/css/w3-theme.css
@@ -0,0 +1,22 @@
+.w3-theme-l5 {color:#000 !important; background-color:#fbfdfe !important}
+.w3-theme-l4 {color:#000 !important; background-color:#f3f8fc !important}
+.w3-theme-l3 {color:#000 !important; background-color:#e6f1f9 !important}
+.w3-theme-l2 {color:#000 !important; background-color:#dae9f6 !important}
+.w3-theme-l1 {color:#000 !important; background-color:#cee2f3 !important}
+.w3-theme-d1 {color:#000 !important; background-color:#9fc7e8 !important}
+.w3-theme-d2 {color:#000 !important; background-color:#7cb2df !important}
+.w3-theme-d3 {color:#fff !important; background-color:#599ed7 !important}
+.w3-theme-d4 {color:#fff !important; background-color:#368ace !important}
+.w3-theme-d5 {color:#fff !important; background-color:#2a73ae !important}
+
+.w3-theme-light {color:#000 !important; background-color:#fbfdfe !important}
+.w3-theme-dark {color:#fff !important; background-color:#2a73ae !important}
+.w3-theme-action {color:#fff !important; background-color:#2a73ae !important}
+
+.w3-theme {color:#000 !important; background-color:#c2dbf0 !important}
+.w3-text-theme {color:#c2dbf0 !important}
+.w3-border-theme {border-color:#c2dbf0 !important}
+
+.w3-hover-theme:hover {color:#000 !important; background-color:#c2dbf0 !important}
+.w3-hover-text-theme:hover {color:#c2dbf0 !important}
+.w3-hover-border-theme:hover {border-color:#c2dbf0 !important}
\ No newline at end of file
diff --git a/ui/css/w3.css b/ui/css/w3.css
new file mode 100644
index 0000000..822fa75
--- /dev/null
+++ b/ui/css/w3.css
@@ -0,0 +1,235 @@
+/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
+html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
+/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
+html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
+article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item}
+audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline}
+audio:not([controls]){display:none;height:0}[hidden],template{display:none}
+a{background-color:transparent}a:active,a:hover{outline-width:0}
+abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}
+b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000}
+small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
+sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none}
+code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible}
+button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold}
+button,input{overflow:visible}button,select{text-transform:none}
+button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}
+button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}
+button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}
+fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}
+legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}
+[type=checkbox],[type=radio]{padding:0}
+[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}
+[type=search]{-webkit-appearance:textfield;outline-offset:-2px}
+[type=search]::-webkit-search-decoration{-webkit-appearance:none}
+::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}
+/* End extract */
+html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden}
+h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}
+.w3-serif{font-family:serif}.w3-sans-serif{font-family:sans-serif}.w3-cursive{font-family:cursive}.w3-monospace{font-family:monospace}
+h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px}
+hr{border:0;border-top:1px solid #eee;margin:20px 0}
+.w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit}
+.w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc}
+.w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1}
+.w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1}
+.w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center}
+.w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top}
+.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
+.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
+.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
+.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
+.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
+.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
+.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
+.w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none}
+.w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block}
+.w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s}
+.w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%}
+.w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc}
+.w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer}
+.w3-dropdown-hover:hover .w3-dropdown-content{display:block}
+.w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000}
+.w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000}
+.w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1}
+.w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px}
+.w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto}
+.w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%}
+.w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%}
+.w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px}
+.w3-main,#main{transition:margin-left .4s}
+.w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)}
+.w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px}
+.w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto}
+.w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0}
+.w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left}
+.w3-bar .w3-button{white-space:normal}
+.w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0}
+.w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%}
+.w3-responsive{display:block;overflow-x:auto}
+.w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before,
+.w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both}
+.w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%}
+.w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%}
+.w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%}
+.w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%}
+@media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%}
+.w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%}
+.w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}}
+@media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%}
+.w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%}
+.w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}}
+.w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px}
+.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px}
+.w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell}
+.w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom}
+.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
+@media (max-width:1205px){.w3-auto{max-width:95%}}
+@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
+.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
+.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
+.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
+@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
+@media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}}
+@media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}}
+@media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}}
+.w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0}
+.w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2}
+.w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0}
+.w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0}
+.w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)}
+.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)}
+.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)}
+.w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
+.w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
+.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none}
+.w3-display-position{position:absolute}
+.w3-circle{border-radius:50%}
+.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
+.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
+.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
+.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
+.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
+.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
+.w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)}
+.w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)}
+.w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}
+.w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}}
+.w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}}
+.w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}}
+.w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}}
+.w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}}
+.w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}}
+.w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}}
+.w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important}
+.w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1}
+.w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75}
+.w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)}
+.w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)}
+.w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)}
+.w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important}
+.w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important}
+.w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important}
+.w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important}
+.w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important}
+.w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important}
+.w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important}
+.w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important}
+.w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important}
+.w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important}
+.w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important}
+.w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important}
+.w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important}
+.w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important}
+.w3-padding-64{padding-top:64px!important;padding-bottom:64px!important}
+.w3-padding-top-64{padding-top:64px!important}.w3-padding-top-48{padding-top:48px!important}
+.w3-padding-top-32{padding-top:32px!important}.w3-padding-top-24{padding-top:24px!important}
+.w3-left{float:left!important}.w3-right{float:right!important}
+.w3-button:hover{color:#000!important;background-color:#ccc!important}
+.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
+.w3-hover-none:hover{box-shadow:none!important}
+/* Colors */
+.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
+.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
+.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
+.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
+.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
+.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
+.w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important}
+.w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important}
+.w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important}
+.w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important}
+.w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important}
+.w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important}
+.w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important}
+.w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important}
+.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
+.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
+.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
+.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
+.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
+.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
+.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
+.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
+.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
+.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
+.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
+.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
+.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
+.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
+.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
+.w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important}
+.w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important}
+.w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important}
+.w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important}
+.w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important}
+.w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important}
+.w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important}
+.w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important}
+.w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important}
+.w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important}
+.w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important}
+.w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important}
+.w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important}
+.w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important}
+.w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important}
+.w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important}
+.w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important}
+.w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important}
+.w3-text-red,.w3-hover-text-red:hover{color:#f44336!important}
+.w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important}
+.w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important}
+.w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important}
+.w3-text-white,.w3-hover-text-white:hover{color:#fff!important}
+.w3-text-black,.w3-hover-text-black:hover{color:#000!important}
+.w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important}
+.w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important}
+.w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important}
+.w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important}
+.w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important}
+.w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important}
+.w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important}
+.w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important}
+.w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important}
+.w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important}
+.w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important}
+.w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important}
+.w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important}
+.w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important}
+.w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important}
+.w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important}
+.w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important}
+.w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important}
+.w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important}
+.w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important}
+.w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important}
+.w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important}
+.w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important}
+.w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important}
+.w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important}
+.w3-border-black,.w3-hover-border-black:hover{border-color:#000!important}
+.w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important}
+.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
+.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
+.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
+.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
\ No newline at end of file
diff --git a/ui/qv.js b/ui/qv.js
index 6ebe52a..628fbaf 100644
--- a/ui/qv.js
+++ b/ui/qv.js
@@ -17,11 +17,50 @@ var qv_palettes = {
magnitude: d3.scaleSequential([0, 10], d3.interpolateGreens)
}
-// var qv_boxInfo = ["condition","column","row","expr","iri","nullable","order-spec","aggregate","left-graph-node","join-filter","left"];
-
-var qv_cardInfo = [ "subject","predicate","object","graph","value","fragment","row" ] ;
-
-var qv_boxInfo = [ "column","condition","expr","order-spec","aggregate","join-filter", "content","cross-product", "default-graph","named-graph","varIn","varOut"];
+var qv_colors = {
+ selected: "orange",
+ default: "steelblue"
+}
+// Displayed in the node box with cardinality info
+var qv_cardInfo = [
+ "subject","predicate","object","graph","value","fragment","row"
+];
+
+// Displayed in the node box
+var qv_boxInfo = [
+ "column","condition","expr","order-spec","aggregate","join-filter","content",
+ "cross-product", "default-graph","named-graph","varIn","varOut","num-sorted",
+ "limit","offset", "percent", "temperature"
+];
+
+var qv_titleInfo = [ "permutation", "type"];
+
+// Displayed in the cost banner
+var qv_costInfo = [
+ "cost","mem-cost","dmem-cost","io-cost","cpu-cost","dcpu-cost","nw-cost","estimated-count"
+];
+
+// Displayed in the execution banner
+var qv_executionInfo = [
+ "count","local-time","remote-time","local-max-memory","remote-max-memory"
+];
+
+// Tooltip row order
+var qv_tooltipPriority = [
+ "id","type","name","schema","view","column-index",
+ "op","permutation","descending","dedup",
+ "left","right",
+ "aggregate",
+ "order","num-sorted",
+ "ltime","rtime","lmem","rmem",
+ "cost","mem","dmem","cpu","dcpu","nw","io","count"
+];
+
+var qv_logTabs = {
+ "optimization" : "Optimization",
+ "estimate" : "Final Plan",
+ "execution": "Execution"
+};
function qv_debug(msg) {
if (qv_box.debug) {
@@ -29,6 +68,115 @@ function qv_debug(msg) {
}
}
+
+function qv_displayTabs (containerid, viewerid, file, data) {
+ d3.select(containerid).select("div").remove()
+ var bar = d3.select(containerid).append("div").attr("class","w3-bar w3-theme-l1")
+ const types = Object.keys(qv_logTabs);
+
+ types.forEach (
+ type => {
+ var a = bar.append("a")
+ if (type === "execution") {
+ a.attr("class", "w3-bar-item w3-button w3-small w3-theme-action")
+ } else {
+ a.attr("class", "w3-bar-item w3-button w3-small w3-theme-d1")
+ }
+ a.attr("href","#")
+ .text(qv_logTabs[type])
+ .on("click",(event) => {
+ bar.selectAll('a[class = "w3-bar-item w3-button w3-small w3-theme-action"]').attr("class", "w3-bar-item w3-button w3-small w3-theme-d1")
+ d3.select(event.target).attr("class", "w3-bar-item w3-button w3-small w3-theme-action")
+ qv_loadFromLog(viewerid, file, data.key, type)
+ })
+ })
+ var message = "Displaying Plan: "
+ if (data.trace) {message += data.trace} else {message += data.key}
+ bar.append("span").style("float","right").attr("class","w3-bar-item w3-small w3-right-align w3-orange").text(message)
+}
+
+
+function qv_visualizeTicks(scale, tickArguments, box) {
+ const width = box.width;
+ const height = box.height,
+ m = 10;
+ if (tickArguments === undefined) tickArguments = [];
+ scale.range([m, height - m]);
+ const svg = d3.create("svg")
+ .attr("width", width)
+ .attr("height", height);
+ svg.append("g")
+ .attr("transform", "translate(" + (width - 60) + ",0)")
+ .call(d3.axisRight(scale).ticks(...tickArguments));
+ return svg
+ }
+
+function qv_jitter (range, unit) {
+ return Math.floor((Math.random() * (range / unit)) * unit)
+}
+
+function qv_displayPlansAsTimeline (containerid, viewerid, file, data) {
+ d3.select(containerid).select("svg").remove()
+ times = d3.group(data, d => Date.parse(d.time))
+ var extent = d3.extent(times.keys())
+ extent[0]=extent[0]-(1000 * 30)
+ extent[1]=extent[1]+(1000 * 30)
+ dst = d3
+ .scaleTime()
+ .domain(extent)
+
+ const container = d3.select(containerid)
+ const box = container.node().getBoundingClientRect()
+
+ box.height = d3.select("#plans").node().parentNode.getBoundingClientRect().height - d3.select("#form").node().getBoundingClientRect().height - 10
+ const jitter = (box.height / 10)
+ const svg = qv_visualizeTicks(dst, [10, d3.timeFormat("%H:%M:%S")],box);
+
+ svg
+ .selectAll("circle")
+ .data(data)
+ .enter()
+ .append("circle")
+ .attr("r", function (d) { return 3})
+ .attr("fill", qv_colors.default)
+ .attr("cy", function (d) { return dst(Date.parse(d.time ) + qv_jitter(3000, 500))})
+ .attr("cx", function (d) { return 10 + qv_jitter (box.width - 100 , 5 )})
+ .on("click",function (event, d) {
+
+ svg.selectAll('circle[fill = "' + qv_colors.selected+ '"]').attr("fill", qv_colors.default)
+ d3.select(event.target).attr("fill", qv_colors.selected)
+
+ qv_displayTabs("#tabs", viewerid, file, d)
+ qv_loadFromLog(viewerid, file, d.key, "execution")
+ }
+ ).each( function (d) {qv_tooltipEvents (d3.select(this),d)})
+ container.append((d) => { return svg.node()})
+
+}
+
+function qv_scanLogForPlans (containerid, fileid, startid, traceid,viewerid) {
+
+ var file=d3.select(fileid).node().value
+ var start= ""
+ var trace=""
+ if (d3.select(startid).node().value) { start = "&start=" + d3.select(startid).node().value.replace(" ","T") }
+ if (d3.select(traceid).node().value) { trace = "&trace=" + d3.select(traceid).node().value }
+ d3.select(containerid).select("table").remove()
+ qv_debug(file + start +trace)
+
+ d3.json('api/scan-for-plans.xqy?regex=+plan%3d&file=' +file + start +trace).then(function(data){
+ qv_displayPlansAsTimeline(containerid, viewerid, file, data)
+
+ })
+}
+
+function qv_loadFromLog (viewerid,file, id, type) {
+ d3.select(viewerid).select("svg").remove()
+ d3.json('api/plan-from-log.xqy?file=' +file+ "&id=" +id + "&type=" +type).then(function(data){
+ qv_showPlan(viewerid, data);
+ })
+}
+
function qv_hierarchy(json) {
var data = d3.stratify()
.id(function (d) { return d._id; })
@@ -43,10 +191,10 @@ function qv_buildTree(nodes, width, height) {
return treemap(nnodes)
}
-function qv_tooltipEvents(element, tooltip) {
+function qv_tooltipEvents(element, tooltip, doFilter) {
if(tooltip) element
.on("mouseover", function(event, d) {
- qv_tooltipShow(event,tooltip);
+ qv_tooltipShow(event,tooltip,doFilter);
})
.on("mouseout", function(event, d) {
qv_tooltipHide(event);
@@ -54,7 +202,7 @@ function qv_tooltipEvents(element, tooltip) {
}
function qv_title(div, title, tooltip) {
- var span = div.append("xhtml:p")
+ var para = div.append("xhtml:p")
.attr("class", "tree-node")
.style("color", "#202020")
.style("padding", "4px")
@@ -62,8 +210,8 @@ function qv_title(div, title, tooltip) {
.style("font-size",qv_box.titleFont)
.style("font-weight", "bold")
.text(title);
- qv_tooltipEvents(span,tooltip);
- return span;
+ qv_tooltipEvents(para,tooltip,true);
+ return para;
}
function qv_row(table, title, text, tooltip, color) {
@@ -78,7 +226,7 @@ function qv_row(table, title, text, tooltip, color) {
}
td1.append("xhtml:span").style("font-weight", "bold").text(title + (text ? ":" : ""));
if (text) td2.append("xhtml:span").text(text)
- qv_tooltipEvents(row,tooltip);
+ qv_tooltipEvents(row,tooltip,false);
return row;
}
@@ -98,29 +246,32 @@ function qv_cost(div, percent) {
// splits parallel and serial cost, and returns values as proportion of a given whole.
-function qv_parallelVsSerial (value, whole) {
- if (value) {
- var components= value.split("/").map(x=>parseFloat(x))
- var parallel = (components[0] + components[1] + components[2]) /whole.parallel
- var serial = (components[3] + components[4]) / whole.serial
- return {parallel:parallel,serial:serial}
- } else {
- return {parallel:0,serial:0}
- }
-}
-function qv_maxParallelVsSerial (value, max) {
- value = qv_parallelVsSerial(value, {parallel:1,serial:1})
- maxp = Math.max (value.parallel, max.parallel)
- maxs = Math.max (value.serial, max.serial)
- return {parallel:maxp,serial:maxs}
-}
-
-function qv_proportion (value, max) {
+function qv_proportion(value, max) {
+ if(typeof max === "object") {
+ var result = {};
+ Object.keys(max).forEach((k) => {
+ result[k] = qv_proportion(value[k],max[k]);
+ });
+ return result;
+ }
if (max == 0) return 0
return value/max
}
+function qv_max(a,b) {
+ if(!a) return b;
+ if(!b) return a;
+ if(typeof a === "object") {
+ var result = {};
+ Object.keys(a).forEach((k) => {
+ result[k] = qv_max(a[k],b[k]);
+ });
+ return result;
+ }
+ return Math.max(a,b);
+}
+
function qv_parseMemory(str) {
var r = parseFloat(str)
if (str.endsWith("Gb")) return r * (1024^3)
@@ -135,74 +286,39 @@ function qv_parseTime(str) {
}
// compute maximum of all the metrics across the plan
+function qv_fetchCost(data) {
+ var fetchParallelSerial = (value) => {
+ if(!value) return {parallel:0,serial:0};
-function qv_maxCost (nodes) {
- var maxcost = {
- label : "maxcost",
- cost: 0,
- mem: 0,
- dmem: 0,
- io: {parallel:0,serial:0},
- cpu: {parallel:0,serial:0},
- dcpu: {parallel:0,serial:0},
- nw: {parallel:0,serial:0},
- count: 0,
- ltime: 0,
- rtime: 0,
- lmem:0,
- rmem:0
- }
-
- nodes.descendants().forEach(element => {
- var data = element.data.data
- if (data.cost) {
- maxcost.cost = Math.max(parseFloat(data.cost),maxcost.cost)
- maxcost.mem = Math.max(parseFloat(data["mem-cost"]), maxcost.mem)
- maxcost.dmem = Math.max(parseFloat(data["dmem-cost"]), maxcost.dmem)
- maxcost.count = Math.max(parseFloat(data["estimated-count"]), maxcost.count)
- maxcost.io = qv_maxParallelVsSerial(data["io-cost"], maxcost.io)
- maxcost.cpu = qv_maxParallelVsSerial(data["cpu-cost"], maxcost.cpu)
- maxcost.dcpu = qv_maxParallelVsSerial(data["dcpu-cost"], maxcost.dcpu)
- maxcost.nw = qv_maxParallelVsSerial(data["nw-cost"], maxcost.nw)
- } else if (data["local-time"]) {
- maxcost.count = Math.max(parseFloat(data.count),maxcost.count)
- maxcost.lmem = Math.max(qv_parseMemory(data["local-max-memory"]), maxcost.lmem)
- maxcost.rmem = Math.max(qv_parseMemory(data["remote-max-memory"]), maxcost.rmem)
- maxcost.ltime = Math.max(qv_parseTime(data["local-time"]), maxcost.ltime)
- maxcost.rtime = Math.max(qv_parseTime(data["remote-time"]), maxcost.rtime)
- }
- })
- return maxcost
-}
-
-
-
-function qv_relativeCost( data, maxcost) {
- if (data.cost) {
- return {
- id: data._id,
- cost: qv_proportion(parseFloat(data.cost) , maxcost.cost),
- mem: qv_proportion(parseFloat(data["mem-cost"]) , maxcost.mem),
- dmem: qv_proportion(parseFloat(data["dmem-cost"]) , maxcost.dmem),
- io: qv_parallelVsSerial(data["io-cost"],maxcost.io),
- cpu: qv_parallelVsSerial(data["cpu-cost"],maxcost.cpu),
- dcpu: qv_parallelVsSerial(data["dcpu-cost"],maxcost.dcpu),
- nw: qv_parallelVsSerial(data["nw-cost"],maxcost.nw),
- count: qv_proportion(parseFloat(data["estimated-count"]) , maxcost.count)
- }
- } else if (data["local-time"]) {
- return {
- id: data._id,
- count: qv_proportion(parseFloat(data.count) , maxcost.count),
- ltime: qv_proportion(qv_parseTime(data["local-time"]) , maxcost.ltime),
- rtime: qv_proportion(qv_parseTime(data["remote-time"]) , maxcost.rtime),
- lmem: qv_proportion(qv_parseMemory(data["local-max-memory"]) , maxcost.lmem),
- rmem: qv_proportion(qv_parseMemory(data["remote-max-memory"]) , maxcost.rmem)
- }
- }
-
-}
+ var components= value.split("/").map(x=>parseFloat(x));
+ var parallel = components[0] + components[1] + components[2];
+ var serial = components[3] + components[4];
+ return {parallel:parallel,serial:serial};
+ };
+ var result = null;
+
+ if (data.cost) result = {
+ // id: data._id,
+ cost: parseFloat(data.cost),
+ mem: parseFloat(data["mem-cost"]),
+ dmem: parseFloat(data["dmem-cost"]),
+ io: fetchParallelSerial(data["io-cost"]),
+ cpu: fetchParallelSerial(data["cpu-cost"]),
+ dcpu: fetchParallelSerial(data["dcpu-cost"]),
+ nw: fetchParallelSerial(data["nw-cost"]),
+ count: parseFloat(data["estimated-count"])
+ };
+ else if (data["local-time"]) result = {
+ // id: data._id,
+ count: parseFloat(data.count),
+ ltime: qv_parseTime(data["local-time"]),
+ rtime: qv_parseTime(data["remote-time"]),
+ lmem: qv_parseMemory(data["local-max-memory"]),
+ rmem: qv_parseMemory(data["remote-max-memory"])
+ };
+ return result;
+}
function qv_displayCostBox (parent , rect, palette, value, prefix="") {
var r = 2;
@@ -217,14 +333,14 @@ function qv_displayCostBox (parent , rect, palette, value, prefix="") {
.attr("height", qv_box.lineHeight).append("title").text(prefix + Math.round(value*100) +"%")
}
-function qv_displayCost(metrics, cost) {
+function qv_displayCost(metrics, cost, maxcost) {
var parent = d3.create("svg:g").attr("width", qv_box.width )
var i = 0;
var padding = 4
var width = (qv_box.width -(padding*2)) / metrics.length;
metrics.forEach (
v => {
- value = cost[v]
+ value = qv_proportion(cost[v],maxcost[v])
x= i * width + padding
tx= i * width + padding + width/2
rx= i * width + padding
@@ -249,10 +365,11 @@ function qv_displayCost(metrics, cost) {
}
}
)
+ qv_tooltipEvents(parent,cost,false);
return parent.node()
}
-function qv_parse_cardinalities(cardinalities) {
+function qv_parseCardinalities(cardinalities) {
if (cardinalities) {
if (cardinalities.startsWith("(")) {
var l=cardinalities.length;
@@ -261,17 +378,17 @@ function qv_parse_cardinalities(cardinalities) {
} else return []
}
-function qv_triple_info(table, type, value, cardinalities) {
+function qv_tripleInfo(table, type, value, cardinalities) {
value = qv_decode(value)
var v= parseInt(value.split(' ')[0])
if (v >= 0) {
var colors = null;
- if (cardinalities[v]) colors = cardinalities[v].map( (x) => qv_palettes.magnitude(Math.round(Math.log10(parseFloat(x)))))
- return qv_row(table, type, value , {
- type : type,
- value : value,
- cardinality: cardinalities[v]}
- , colors)
+ var tooltip = value;
+ if (cardinalities[v]) {
+ colors = cardinalities[v].map( (x) => qv_palettes.magnitude(Math.round(Math.log10(parseFloat(x)))));
+ tooltip = { value : value, cardinality: cardinalities[v].join(",") };
+ }
+ return qv_row(table,type,value,tooltip,colors);
}
else return qv_row(table, type, value, value)
}
@@ -298,10 +415,12 @@ function qv_node(node) {
var data = node.data.data
var div = d3.create("div")
var name = data._name
- if ( data.permutation) name += " (" + data.permutation + ")"
- if ( data.type) name += " (" + data.type + ")"
- if ( data.limit) name += " (" + data.limit + ")"
- if ( data["num-sorted"]) name += " ( num-sorted=" + data["num-sorted"] + ")"
+ qv_titleInfo.filter((key) => data.hasOwnProperty(key))
+ .forEach((key) => {name += " (" + data[key] + ")"});
+ //if ( data.permutation) name += " (" + data.permutation + ")"
+ //if ( data.type) name += " (" + data.type + ")"
+ // if ( data.limit) name += " (" + data.limit + ")"
+ // if ( data["num-sorted"]) name += " ( num-sorted=" + data["num-sorted"] + ")"
qv_title(div, name, data)
.on("click", (event) => {
if (node.children) {
@@ -319,11 +438,11 @@ function qv_node(node) {
.style("border","0px")
.style("font-size", qv_box.font);
- var cardinalities = qv_parse_cardinalities(data.cardinalities)
- //console.log(cardinalities)
+ var cardinalities = qv_parseCardinalities(data.cardinalities)
+
qv_cardInfo
.filter((key) => data.hasOwnProperty(key))
- .forEach((key) => qv_triple_info(table,key,data[key],cardinalities));
+ .forEach((key) => qv_tripleInfo(table,key,data[key],cardinalities));
qv_boxInfo
.filter((key) => data.hasOwnProperty(key))
.forEach((key) => qv_row(table,key,data[key],data[key]));
@@ -339,30 +458,77 @@ function qv_nodeHeight(data) {
qv_boxInfo
.filter((key) => data.hasOwnProperty(key))
.forEach((key) => { size += linesize });
- if(data.cost || data.count) size = size + 2 * linesize
+ if(qv_costInfo.some((v) => data.hasOwnProperty(v)))
+ size = size + 2 * linesize
+ if(qv_executionInfo.some((v) => data.hasOwnProperty(v)))
+ size = size + 2 * linesize
return size
}
-function qv_tooltipShow(event, data) {
- var tooltip = d3.select("#tooltip")
- tooltip.style("left", (event.pageX + 28) + "px")
- .style("top", (event.pageY - 28) + "px");
- tooltip.select("pre").text(JSON.stringify(data, null,2))
- tooltip.transition()
- .duration(200)
- .style("opacity", .9);
-
-}
+function qv_tooltipTableRow(table, key, value) {
+ if(Array.isArray(value)) {
+ value.forEach((v) => qv_tooltipTableRow(table,key,v));
+ }
+ else if(typeof(value)==="object") {
+ var tr = table.append("tr");
+ if(key!==null) tr.append("td").text(key);
+ var table2 = tr.append("td").append("table");
+ Object.keys(value).forEach((key) => qv_tooltipTableRow(table2,key,value[key]));
+ }
+ else {
+ var tr = table.append("tr");
+ if(key!==null) tr.append("td").text(key);
+ if(typeof(value)==="string") value = qv_decode(value);
+ tr.append("td").text(value);
+ }
+}
+function qv_tooltipContents(parent, data, doFilter) {
+ if(!Array.isArray(data) && typeof(data)==="object") {
+ var seen = {};
+ if(doFilter) {
+ Object.keys(data).filter((key) => key.charAt(0)=='_')
+ .forEach((key) => { seen[key] = true });
+ qv_cardInfo.forEach((key) => { seen[key] = true });
+ qv_boxInfo.forEach((key) => { seen[key] = true });
+ qv_costInfo.forEach((key) => { seen[key] = true });
+ qv_executionInfo.forEach((key) => { seen[key] = true });
+ }
-function qv_tooltipHide(event) {
+ var seenFilter = (key) => {
+ if(seen[key]) return false;
+ seen[key] = true;
+ return data.hasOwnProperty(key);
+ };
+
+ var table = parent.append("table");
+ var display = (key) => qv_tooltipTableRow(table,key,data[key]);
+
+ qv_tooltipPriority.filter(seenFilter).forEach(display);
+ Object.keys(data).filter(seenFilter).forEach(display);
+ }
+ else {
+ qv_tooltipTableRow(parent.append("table"),null,data);
+ }
+}
+
+function qv_tooltipShow(event, data, doFilter) {
var tooltip = d3.select("#tooltip")
- tooltip.transition()
- .duration(500)
- .style("opacity", 0)
-
+ .style("left", (event.pageX + 28) + "px")
+ .style("top", (event.pageY - 28) + "px");
+ tooltip.html("");
+ qv_tooltipContents(tooltip,data,doFilter);
+ tooltip.transition()
+ .duration(200)
+ .style("opacity", .9);
}
+function qv_tooltipHide(event) {
+ var tooltip = d3.select("#tooltip");
+ tooltip.transition()
+ .duration(500)
+ .style("opacity", 0);
+}
function qv_isVisible(node) {
var id=node.data.id
@@ -380,35 +546,36 @@ function qv_toggle(nodes, visibility) {
})
}
-function qv_init(containerid, json) {
+function qv_showPlan(containerid, json) {
qv_debug(json)
var container = d3.select(containerid);
- var margin = { top: 40, right: 90, bottom: 200, left: 90 }
+ var margin = { top: 20, right: 0, bottom: 0, left: 0 }
var nodes = qv_hierarchy(json)
- var maxcost = qv_maxCost(nodes)
-
- qv_debug(maxcost);
-
- var width = (nodes.leaves().length + 2) * (qv_box.width + qv_box.hpadding);
- var height = nodes.height * (qv_box.height + qv_box.vpadding) * 2;
+ //var width = (nodes.leaves().length + 2) * (qv_box.width + qv_box.hpadding);
+ //var height = nodes.height * (qv_box.height + qv_box.vpadding) * 2;
+ const box = container.node().getBoundingClientRect()
+ var width = box.width
+ var height = box.height - 40
nodes = qv_buildTree(nodes,width,height)
qv_debug(nodes);
var svg = container.append("svg")
- .attr("width", Math.max(2048, width + margin.left + margin.right))
- .attr("height", height + margin.top + margin.bottom)
+ .attr("width", width + margin.left + margin.right)
+ .attr("height", height + margin.top + margin.bottom)
// set up transform and zoom
g = svg.append("g")
- .attr("transform",
- "translate(" + ( margin.left) + "," + margin.top + ")");
+ .attr("transform", d3.zoomIdentity.translate(width/2, 10));
+
var zoom = d3.zoom()
.scaleExtent([0.1, 10])
.on('zoom', function (event) {
+
g.attr('transform', event.transform);
});
svg.call(zoom);
+
//links
qv_debug(nodes.links())
@@ -451,7 +618,7 @@ function qv_init(containerid, json) {
.append("animateMotion")
.attr("calcMode","linear")
.attr("rotate","auto")
- .attr("keyPoints","0.3;0.3")
+ .attr("keyPoints","0.6;0.6")
.attr("keyTimes","0.0;1.0")
.append("mpath")
.attr("href", function(d) { return "#link_" +d.target.data.id; });
@@ -481,23 +648,35 @@ function qv_init(containerid, json) {
return h})
// add cost banner
- node.filter(function(d){ return d.data.data.cost }).append((d) => {
- var cost = qv_relativeCost(d.data.data, maxcost)
- return qv_displayCost(["cost","mem","dmem","io","cpu", "dcpu","nw", "count"],cost)
- })
- node.filter(function(d){ return d.data.data["local-time"] }).append((d) => {
- var cost = qv_relativeCost(d.data.data, maxcost)
- qv_debug(cost)
- return qv_displayCost(["ltime","rtime", "lmem","rmem", "count"],cost)
- })
+ var maxcost = null;
+ nodes.descendants().forEach(element => {
+ maxcost = qv_max(qv_fetchCost(element.data.data),maxcost);
+ });
+ qv_debug(maxcost);
+
+ node.filter((d) => qv_costInfo.some((v) => d.data.data[v])).append((d) => {
+ var cost = qv_fetchCost(d.data.data);
+ return qv_displayCost(["cost","mem","dmem","cpu","dcpu","nw","io","count"],cost,maxcost);
+ });
+ node.filter((d) => qv_executionInfo.some((v) => d.data.data[v])).append((d) => {
+ var cost = qv_fetchCost(d.data.data);
+ qv_debug(cost);
+ return qv_displayCost(["ltime","rtime","lmem","rmem","count"],cost,maxcost);
+ });
// add text box
var fo = node.append("foreignObject")
- .attr("y", function (d) { if (d.data.data.cost || d.data.data["local-time"] ) {return qv_box.lineHeight * 2} else {return 0}})
+ .attr("y", (d) => {
+ var result = 0;
+ if(qv_costInfo.some((v) => d.data.data.hasOwnProperty(v)))
+ result += qv_box.lineHeight * 2;
+ if(qv_executionInfo.some((v) => d.data.data.hasOwnProperty(v)))
+ result += qv_box.lineHeight * 2;
+ return result;
+ })
.attr("height", function (d) { return qv_nodeHeight(d.data.data) })
.attr("width", qv_box.width)
fo.append((d) => {
return qv_node(d, maxcost);
- }
- )
+ })
}