Skip to content
Permalink
Browse files

add node descriptions, clean up plan-node.ts

  • Loading branch information
AlexTatiyants committed Jan 5, 2016
1 parent d89ddcf commit 9554ab8c51c0a5853a58be98936223908eabcbb9

Large diffs are not rendered by default.

@@ -51,3 +51,9 @@ a {
.clickable {
cursor: pointer;
}


code {
font-family: $font-family-mono;
font-weight: 600;
}
@@ -9,6 +9,10 @@
z-index: 4;
left: 0;

code {
font-weight: 300;
}

.plan-query-text {
background-color: #fff;
font-family: $font-family-mono;
@@ -41,7 +41,6 @@
.prop-list {
float: left;
text-align: left;
width: 400px;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
@@ -51,7 +50,6 @@

.relation-name {
text-align: left;
max-width: 220px;
}

.planner-estimate {
@@ -83,6 +81,20 @@
&:hover {
border-color: $highlight-color;
}

.node-description {
text-align: left;
font-style: italic;
padding-top: $padding-lg;
word-break: normal;

.node-type {
font-weight: 600;
background-color: $blue;
color: #fff;
padding: 0 $padding-base;
}
}
}

.node-bar-container {
@@ -110,3 +122,7 @@
text-align: left;
display: block;
}

.expanded {
width: 400px;
}

Large diffs are not rendered by default.

@@ -1,26 +1,30 @@
<div class="plan-node">
<div class="plan-node" [class.expanded]="showDetails">
<header (click)="showDetails = !showDetails">
<h4>{{node['Node Type'] | uppercase}}
<h4>{{node[_planService.NODE_TYPE_PROP] | uppercase}}
</h4>
<span class="node-duration">{{duration}}<span class="text-muted">{{durationUnit}} | </span>
<strong>{{executionTimePercent}}</strong><span class="text-muted">%</span>
</span>
</header>

<div class="relation-name" *ngIf="node['Relation Name']">
<div class="relation-name" *ngIf="node[_planService.RELATION_NAME_PROP]">
<button *ngIf="plan.formattedQuery" class="btn btn-sm btn-default btn-slim pull-right" (click)="showQuery = !showQuery">
<i class="fa fa-database"></i>
</button>

<span class="text-muted">on </span>
<span *ngIf="node['Schema']">{{node['Schema']}}.</span>{{node['Relation Name']}}
<span *ngIf="node['Alias']"> ({{node['Alias']}})</span>
<span *ngIf="node[_planService.SCHEMA_PROP]">{{node[_planService.SCHEMA_PROP]}}.</span>{{node[_planService.RELATION_NAME_PROP]}}
<span *ngIf="node[_planService.ALIAS_PROP]"> ({{node[_planService.ALIAS_PROP]}})</span>
</div>

<div class="relation-name" *ngIf="node['Group Key']"><span class="text-muted">by</span> {{node['Group Key']}}</div>
<div class="relation-name" *ngIf="node['Sort Key']"><span class="text-muted">by</span> {{node['Sort Key']}}</div>
<div class="relation-name" *ngIf="node['Join Type']">{{node['Join Type']}} <span class="text-muted">join</span></div>
<div class="relation-name" *ngIf="node['Index Name']"><span class="text-muted">using</span> {{node['Index Name']}}</div>
<div class="relation-name" *ngIf="node[_planService.GROUP_KEY_PROP]">
<span class="text-muted">by</span> {{node[_planService.GROUP_KEY_PROP]}}</div>
<div class="relation-name" *ngIf="node[_planService.SORT_KEY_PROP]">
<span class="text-muted">by</span> {{node[_planService.SORT_KEY_PROP]}}</div>
<div class="relation-name" *ngIf="node[_planService.JOIN_TYPE_PROP]">{{node[_planService.JOIN_TYPE_PROP]}}
<span class="text-muted">join</span></div>
<div class="relation-name" *ngIf="node[_planService.INDEX_NAME_PROP]"><span class="text-muted">
using</span> {{node[_planService.INDEX_NAME_PROP]}}</div>

<div class="tags" *ngIf="viewOptions.showTags && tags.length > 0">
<span *ngFor="#tag of tags">{{tag}}</span>
@@ -41,12 +45,18 @@ <h4>{{node['Node Type'] | uppercase}}
<span> by <strong>{{plannerRowEstimateValue}}</strong>x</span>
</div>

<table *ngIf="showDetails" class="table prop-list">
<tr *ngFor="#prop of props">
<td width="40%">{{prop.key}}</td>
<td>{{prop.value}}</td>
<tr>
</table>
<div *ngIf="showDetails">
<div *ngIf="getNodeTypeDescription()" class="node-description">
<span class="node-type">{{node[_planService.NODE_TYPE_PROP]}} Node</span> <span [innerHtml]="getNodeTypeDescription()"></span>
</div>

<table class="table prop-list">
<tr *ngFor="#prop of props">
<td width="40%">{{prop.key}}</td>
<td>{{prop.value}}</td>
<tr>
</table>
</div>

<div *ngIf="showQuery" class="plan-query-container">
<button class="btn btn-close pull-right" (click)="showQuery = false">
@@ -1,16 +1,20 @@
import {IPlan} from '../../interfaces/iplan';
import {Component, OnInit} from 'angular2/core';
import {HighlightType, EstimateDirection} from '../../enums';

import {PlanService} from '../../services/plan-service';
import {SyntaxHighlightService} from '../../services/syntax-highlight-service';
import {HelpService} from '../../services/help-service';
import {ColorService} from '../../services/color-service';

/// <reference path="lodash.d.ts" />

@Component({
selector: 'plan-node',
inputs: ['plan', 'node', 'viewOptions'],
templateUrl: './components/plan-node/plan-node.html',
directives: [PlanNode],
providers: [PlanService, SyntaxHighlightService]
providers: [PlanService, SyntaxHighlightService, HelpService, ColorService]
})

export class PlanNode {
@@ -44,7 +48,11 @@ export class PlanNode {
estimateDirections = EstimateDirection;
highlightTypes = HighlightType;

constructor(private _planService: PlanService, private _syntaxHighlightService: SyntaxHighlightService) { }
constructor(private _planService: PlanService,
private _syntaxHighlightService: SyntaxHighlightService,
private _helpService: HelpService,
private _colorService: ColorService)
{}

ngOnInit() {
this.currentHighlightType = this.viewOptions.highlightType;
@@ -67,9 +75,9 @@ export class PlanNode {

getFormattedQuery() {
var keyItems: Array<string> = [];
keyItems.push(this.node['Schema'] + '.' + this.node['Relation Name']);
keyItems.push(' ' + this.node['Relation Name'] + ' ');
keyItems.push(' ' + this.node['Alias'] + ' ');
keyItems.push(this.node[this._planService.SCHEMA_PROP] + '.' + this.node[this._planService.RELATION_NAME_PROP]);
keyItems.push(' ' + this.node[this._planService.RELATION_NAME_PROP] + ' ');
keyItems.push(' ' + this.node[this._planService.ALIAS_PROP] + ' ');
return this._syntaxHighlightService.highlightKeyItems(this.plan.formattedQuery, keyItems);
}

@@ -90,7 +98,7 @@ export class PlanNode {
}

if (this.width < 1) { this.width = 1 }
this.backgroundColor = this.numberToColorHsl(1 - this.width / this.MAX_WIDTH);
this.backgroundColor = this._colorService.numberToColorHsl(1 - this.width / this.MAX_WIDTH);
}

calculateDuration() {
@@ -112,7 +120,7 @@ export class PlanNode {
// create an array of node propeties so that they can be displayed in the view
calculateProps() {
this.props = _.chain(this.node)
.omit('Plans')
.omit(this._planService.PLANS_PROP)
.map((value, key) => {
return { key: key, value: value };
})
@@ -135,52 +143,7 @@ export class PlanNode {
}
}

/**
* http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
*
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param Number h The hue
* @param Number s The saturation
* @param Number l The lightness
* @return Array The RGB representation
*/
hslToRgb(h, s, l) {
var r, g, b;

if (s == 0) {
r = g = b = l; // achromatic
} else {
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}

var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}

return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)];
}

// convert a number to a color using hsl
numberToColorHsl(i) {
// as the function expects a value between 0 and 1, and red = 0° and green = 120°
// we convert the input to the appropriate hue value
var hue = i * 100 * 1.2 / 360;
// we convert hsl to rgb (saturation 100%, lightness 50%)
var rgb = this.hslToRgb(hue, .9, .4);
// we format to css value and return
return 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')';
}
getNodeTypeDescription() {
return this._helpService.getNodeTypeDescription(this.node[this._planService.NODE_TYPE_PROP]);
}
}
@@ -0,0 +1,50 @@
export class ColorService {
/**
* http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
*
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param Number h The hue
* @param Number s The saturation
* @param Number l The lightness
* @return Array The RGB representation
*/
hslToRgb(h, s, l) {
var r, g, b;

if (s == 0) {
r = g = b = l; // achromatic
} else {
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}

var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}

return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)];
}

// convert a number to a color using hsl
numberToColorHsl(i) {
// as the function expects a value between 0 and 1, and red = 0° and green = 120°
// we convert the input to the appropriate hue value
var hue = i * 100 * 1.2 / 360;
// we convert hsl to rgb (saturation 100%, lightness 50%)
var rgb = this.hslToRgb(hue, .9, .4);
// we format to css value and return
return 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')';
}
}
@@ -0,0 +1,27 @@
/// <reference path="lodash.d.ts" />

export class HelpService {
getNodeTypeDescription(nodeType: string) {
return NODE_DESCRIPTIONS[nodeType.toUpperCase()];
}
}

export var NODE_DESCRIPTIONS = {
"LIMIT":"returns a specified number of rows from a record set.",
"SORT": "sorts a record set based on the specified sort key.",
"NESTED LOOP": `merges two record sets by looping through every record in the first set and
tries to find a match in the second set. All matching records are returned.`,
"MERGE JOIN": `merges two record sets by first sorting them on a <strong>join key</strong>.`,
"AGGREGATE": `groups records together based on a GROUP BY or aggregate function (like <code>sum()</code>).`,
"HASHAGGREGATE": `groups records together based on a GROUP BY or aggregate function (like sum()). Hash Aggregate uses
a hash to first organize the records by a key.`,
"SEQ SCAN": `finds relevant records by sequentially scanning the table. Seq Scans perform a single read operation
(only the table is read).`,
"INDEX SCAN": `finds relevant records based on an <strong>Index</strong>. Index Scans perform 2 read operations: one to
read the index and another to read the actual value from the table.`,
"INDEX ONLY SCAN": `finds relevant records based on an <strong>Index</strong>. Index Only Scans perform a single read operation
from the index and do not read from the corresponding table.`,
"BITMAP HEAP SCAN": "scans all pages returned by the <strong>Bitmap Index Scan</strong> for relevant rows.",
"BITMAP INDEX SCAN": `uses a <strong>Bitmap Index</strong> (index which uses 1 bit per page) to find all relevant pages.
Results of this node are fed to the <strong>Bitmap Heap Scan</strong>.`
}
@@ -12,7 +12,13 @@ export class PlanService {
ACTUAL_LOOPS_PROP: string = 'Actual Loops';
TOTAL_COST_PROP: string = 'Total Cost';
PLANS_PROP: string = 'Plans';

RELATION_NAME_PROP: string = 'Relation Name';
SCHEMA_PROP: string = 'Schema';
ALIAS_PROP: string = 'Alias';
GROUP_KEY_PROP: string = 'Group Key';
SORT_KEY_PROP: string = 'Sort Key';
JOIN_TYPE_PROP: string = 'Join Type';
INDEX_NAME_PROP: string = 'Index Name';

// computed by pev
COMPUTED_TAGS_PROP: string = "*Tags";

0 comments on commit 9554ab8

Please sign in to comment.
You can’t perform that action at this time.