You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by mb...@apache.org on 2018/06/01 12:44:34 UTC
[06/26] asterixdb git commit: [ASTERIXDB-2318] Build dashboard in mvn
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/indexes-collection/indexes.component.scss
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/indexes-collection/indexes.component.scss b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/indexes-collection/indexes.component.scss
new file mode 100755
index 0000000..15b364a
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/indexes-collection/indexes.component.scss
@@ -0,0 +1,398 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+$indexes-spacing-unit: 8px;
+
+.container {
+ display: flex;
+ flex-flow: row;
+ padding: 0;
+ margin: 0;
+}
+
+.master {
+ width: 60%;
+}
+
+.detail {
+ width: 40%;
+}
+
+.indexes-card {
+ display: flex;
+ flex-flow: column;
+ padding: 0;
+ margin:0 auto;
+ margin-top: ($indexes-spacing-unit * 2);
+ min-height: 450px;
+ max-height: 450px;
+ //min-width: 98%; //(100vw / 2);
+ //max-width: 98%; // (100vw / 2);
+ width: 95%; // 98%;
+ overflow: hidden;
+}
+
+.indexes-details-card {
+ display: flex;
+ flex-flow: column;
+ padding: 0;
+ margin:0 auto;
+ margin: ($indexes-spacing-unit * 2);
+ min-height: 716px;
+ max-height: 716px;
+ //min-width: 95%; //(100vw / 2);
+ //max-width: 95%; // (100vw / 2);
+ overflow: hidden;
+}
+
+.icon {
+ padding: 0 14px 0 0;
+ margin: 0;
+}
+
+.spacer {
+ flex: 1 1 auto;
+}
+
+.indexes-selector {
+ min-height: 42px;
+ max-height: 42px;
+ justify-content: center;
+ //align-items: center;
+ font-size: 0.80rem;
+ font-weight: 500;
+ background-color: white;
+ border: 1px solid rgba(54, 147, 209, 0.87);
+}
+
+.indexes-content {
+ position:relative;
+ top: 0;
+ left: 0;
+ margin: 0px;
+ padding: 0px;
+ overflow: auto;
+}
+
+.indexes-table {
+ margin: $indexes-spacing-unit !important;
+ height: 330px;
+ overflow: auto;
+}
+
+.spacer {
+ flex: 1 1 auto;
+}
+
+//.indexes-toolbar {
+// display: block;
+// min-height: 56px;
+// height: 56px;
+ //width: 250px;
+// font-size: 12px;
+// white-space: nowrap;
+// overflow: hidden;
+// text-overflow: ellipsis;
+// letter-spacing: 1px;
+// font-weight: 400;
+// background: rgba(0,0,1, .80);
+//}
+
+.example-header {
+ min-height: 64px;
+ display: flex;
+ align-items: center;
+ padding-left: 24px;
+ font-size: 20px;
+}
+
+.mat-table {
+ overflow: auto;
+}
+
+.customWidthClass{
+ flex: 0 0 75px;
+}
+
+.mat-column-DataverseName {
+ text-align: left;
+}
+
+.mat-header-cell.mat-column-DataverseName {
+ text-align: left;
+}
+
+.mat-cell.mat-column-DataverseName {
+ text-align: left;
+}
+
+.header-dataversename-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: left;
+ color: hsla(0,0%,0%,.87);
+ text-transform: uppercase;
+ flex: 0 0 150px;
+}
+
+.indexes-dataversename-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: left;
+ color: hsla(0,0%,0%,.87);
+ flex: 0 0 150px;
+}
+
+.header-datasetname-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: left;
+ color: hsla(0,0%,0%,.87);
+ text-transform: uppercase;
+ flex: 0 0 150px;
+}
+
+.indexes-datasetname-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: left;
+ color: hsla(0,0%,0%,.87);
+ flex: 0 0 150px;
+}
+
+.header-indexname-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: left;
+ color: hsla(0,0%,0%,.87);
+ text-transform: uppercase;
+ flex: 0 0 150px;
+}
+
+.indexes-indexname-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: left;
+ color: hsla(0,0%,0%,.87);
+ flex: 0 0 150px;
+}
+
+.header-indexestructure-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: center;
+ color: hsla(0,0%,0%,.87);
+ text-transform: uppercase;
+ flex: 0 0 150px;
+}
+
+.indexes-indexstructure-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: center;
+ color: hsla(0,0%,0%,.87);
+ flex: 0 0 150px;
+}
+
+.header-isprimary-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: center;
+ color: hsla(0,0%,0%,.87);
+ text-transform: uppercase;
+ flex: 0 0 150px;
+}
+
+.indexes-isprimary-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: center;
+ color: hsla(0,0%,0%,.87);
+ flex: 0 0 150px;
+}
+
+.header-timestamp-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: left;
+ color: hsla(0,0%,0%,.87);
+ text-transform: uppercase;
+ flex: 0 0 150px;
+}
+
+.indexes-timestamp-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: left;
+ color: hsla(0,0%,0%,.87);
+ flex: 0 0 150px;
+}
+
+.header-groupname-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: left;
+ color: hsla(0,0%,0%,.87);
+ text-transform: uppercase;
+ flex: 0 0 150px;
+}
+
+.indexes-groupname-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: left;
+ color: hsla(0,0%,0%,.87);
+ flex: 0 0 150px;
+}
+
+.header-timestamp-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: left;
+ color: hsla(0,0%,0%,.87);
+ text-transform: uppercase;
+ flex: 0 0 250px;
+}
+
+.indexes-timestamp-cell {
+ border: none;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: $indexes-spacing-unit * 3;
+ font-weight: 400;
+ margin: 0;
+ padding: 0 ($indexes-spacing-unit * 2);
+ text-align: left;
+ color: hsla(0,0%,0%,.87);
+ flex: 0 0 250px;
+}
+
+.example-header {
+ min-height: 56px;
+ max-height: 56px;
+ display: flex;
+ align-items: center;
+ padding: 8px 24px 0;
+ font-size: 20px;
+ justify-content: space-between;
+ border-bottom: 1px solid transparent;
+}
+
+.mat-form-field {
+ font-size: 14px;
+ flex-grow: 1;
+ margin-top: 8px;
+}
+
+.example-no-results {
+ display: flex;
+ justify-content: center;
+ padding: 24px;
+ font-size: 12px;
+ font-style: italic;
+}
+
+.actions {
+ display: flex;
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+ color: rgba(54, 147, 209, 0.87);
+ padding: $indexes-spacing-unit;
+ margin: 0;
+}
+
+.error-message {
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+ color: rgba(209, 54, 54, 0.87);
+ padding-top: 10px;
+ padding-left: 20px;
+ text-overflow: ellipsis;
+}
+
+.output {
+ padding-left: ($indexes-spacing-unit * 2);
+}
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/indexes-collection/indexes.component.ts
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/indexes-collection/indexes.component.ts b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/indexes-collection/indexes.component.ts
new file mode 100755
index 0000000..2913470
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/indexes-collection/indexes.component.ts
@@ -0,0 +1,230 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+import { Component, Inject, Input } from '@angular/core';
+import { Index } from '../../../shared/models/asterixDB.model'
+import { Store } from '@ngrx/store';
+import { Observable } from 'rxjs/Observable';
+import * as indexActions from '../../../shared/actions/index.actions'
+import { ElementRef, ViewChild} from '@angular/core';
+import {DataSource} from '@angular/cdk/collections';
+import {BehaviorSubject} from 'rxjs/BehaviorSubject';
+import 'rxjs/add/operator/startWith';
+import 'rxjs/add/observable/merge';
+import 'rxjs/add/operator/map';
+import 'rxjs/add/operator/debounceTime';
+import 'rxjs/add/operator/distinctUntilChanged';
+import 'rxjs/add/observable/fromEvent';
+import { Subscription } from "rxjs/Rx";
+import { State } from '../../../shared/reducers/index.reducer';
+import { MatPaginator } from '@angular/material';
+import { SelectionModel } from '@angular/cdk/collections';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
+
+/**
+ * Root component
+ * Defines our application's layout
+ */
+@Component({
+ selector: 'awc-indexes',
+ templateUrl: 'indexes.component.html',
+ styleUrls: ['indexes.component.scss']
+})
+
+export class IndexCollection {
+ displayedColumns = "['dataverseName', 'datasetName', 'indexName', 'indexStructure', 'isPrimary', 'timestamp', 'pendingOp']"
+ /*
+ dataverseName: string;
+ datasetName: string;
+ indexName: string;
+ indexStructure: string;
+ searchKey: string[];
+ isPrimary: boolean;
+ timestamp: string;
+ pendingOp: string;
+ */
+ indexName: string;
+ dataSource: IndexDataSource | null;
+ loaded$: Observable<any>;
+ @Input('message') errorMessage: string = ""
+
+ idxName = "";
+
+ constructor(private store: Store<any>, public dialog: MatDialog) {
+ this.loaded$ = this.store.select('index');
+ // Watching the name of the latest create index
+ this.store.select(s => s.index.createIndexName).subscribe((data: any) => {
+ this.idxName = data;
+ })
+
+ // Watching the name of the latest drop index
+ this.store.select(s => s.index.dropIndexName).subscribe((data: any) => {
+ this.idxName = data;
+ })
+
+ // Watching for the if there is a change in the collection
+ this.store.select(s => s.index.createIndexSuccess).subscribe((data: any) => {
+ if (data === true) {
+ this.getIndexes();
+ this.errorMessage = "SUCCEED: CREATE INDEX " + this.idxName;
+ }
+ })
+
+ // Watching for the if there is a error in a create index operation
+ this.store.select(s => s.index.createIndexError).subscribe((data: any) => {
+ if (data.errors) {
+ this.errorMessage = "ERROR: " + data.errors[0].msg;
+ }
+ })
+
+ // Watching for the success message in a drop index operation
+ this.store.select(s => s.index.dropIndexSuccess).subscribe((data: any) => {
+ if (data === true) {
+ this.getIndexes();
+ this.errorMessage = "SUCCEED: DROP INDEX " + this.idxName;
+ }
+ })
+
+ // Watching for the if there is a error in a drop index operation
+ this.store.select(s => s.index.dropIndexError).subscribe((data: any) => {
+ if (data.errors) {
+ this.errorMessage = "ERROR: " + data.errors[0].msg;
+ }
+ })
+ }
+
+ getIndexes() {
+ // Triggers the effect to refresg the indexes
+ this.store.dispatch(new indexActions.SelectIndexes('-'));
+ }
+
+ ngOnInit() {
+ // Assign the datasource for the table
+ this.dataSource = new IndexDataSource(this.store);
+ }
+
+ /*
+ * opens the create index dialog
+ */
+ openCreateIndexDialog(): void {
+ let dialogRef = this.dialog.open(DialogCreateIndex, {
+ width: '420px',
+ data: { name: this.indexName }
+ });
+
+ dialogRef.afterClosed().subscribe(result => {
+ this.indexName = result;
+ });
+ }
+
+
+ /*
+ * opens the drop index dialog
+ */
+ openDropIndexDialog(): void {
+ let dialogRef = this.dialog.open(DialogDropIndex, {
+ width: '420px',
+ data: { name: this.indexName }
+ });
+
+ dialogRef.afterClosed().subscribe(result => {
+ this.indexName = result;
+ });
+ }
+
+ /*
+ * Clean up the error message on the screen
+ */
+ onClick(): void {
+ this.errorMessage = "";
+ }
+
+ /* Showing all the index metadata information */
+ output: any;
+
+ highlight(row){
+ this.output = JSON.stringify(row, null, 2);
+ }
+
+ @ViewChild('querymetadata') inputQuery;
+
+ /* Cleans up error message */
+ cleanUp() {
+ this.errorMessage = "";
+ // Cascading
+ this.inputQuery.cleanUp();
+ }
+}
+
+@Component({
+ selector: 'index-create-dialog',
+ templateUrl: 'index-create-dialog.component.html',
+ styleUrls: ['index-create-dialog.component.scss']
+})
+
+export class DialogCreateIndex {
+ constructor( private store: Store<any>,
+ public dialogCreateIdxRef: MatDialogRef<DialogCreateIndex>,
+ @Inject(MAT_DIALOG_DATA) public data: any) { }
+
+ onClick(): void {
+ this.store.dispatch(new indexActions.CreateIndex(this.data.indexName));
+ this.dialogCreateIdxRef.close();
+ }
+
+ onNoClick(): void {
+ this.dialogCreateIdxRef.close();
+ }
+}
+
+@Component({
+ selector: 'index-drop-dialog',
+ templateUrl: 'index-drop-dialog.component.html',
+ styleUrls: ['index-drop-dialog.component.scss']
+})
+
+export class DialogDropIndex {
+ constructor( private store: Store<any>,
+ public dialogDropIdxRef: MatDialogRef<DialogDropIndex>,
+ @Inject(MAT_DIALOG_DATA) public data: any) { }
+
+ onClick(): void {
+ console.log(this.data.indexName)
+ this.store.dispatch(new indexActions.DropIndex(this.data.indexName));
+ this.dialogDropIdxRef.close();
+ }
+
+ onNoClick(): void {
+ this.dialogDropIdxRef.close();
+ }
+}
+
+export class IndexDataSource extends DataSource<any> {
+ private indexes$: Observable<any>
+
+ constructor(private store: Store<any>) {
+ super();
+ this.indexes$ = this.store.select(s => s.index.indexes.results);
+ }
+
+ /** Connect function called by the table to retrieve one stream containing the data to render. */
+ connect(): Observable<Index[]> {
+ const displayDataChanges = [
+ this.indexes$,
+ ];
+
+ return this.indexes$;
+ }
+
+ disconnect() {}
+}
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/input-metadata.component.html
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/input-metadata.component.html b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/input-metadata.component.html
new file mode 100755
index 0000000..939ab06
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/input-metadata.component.html
@@ -0,0 +1,31 @@
+<!--/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/ -->
+<mat-card class="input-card" (click)="onClick()">
+ <mat-toolbar color="primary" class="input-selector">
+ <mat-icon class="toolbar-icon">menu</mat-icon>
+ <span>INPUT: SQL++</span>
+ <span class="spacer"></span>
+ </mat-toolbar>
+ <mat-card-content class="content-area">
+ <div class="codemirror-container">
+ <codemirror-metadata class="code" [(ngModel)]="queryMetadataString" [config]="codemirrorMetadataConfig">
+ </codemirror-metadata>
+ </div>
+ </mat-card-content>
+ <mat-card-actions class="actions">
+ <button mat-button class="query-button" (click)="executeQuery()">RUN</button>
+ <span class="error-message">{{errorMessage}}</span>
+ <span class="spacer"></span>
+ </mat-card-actions>
+</mat-card>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/input-metadata.component.scss
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/input-metadata.component.scss b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/input-metadata.component.scss
new file mode 100755
index 0000000..b98080c
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/input-metadata.component.scss
@@ -0,0 +1,78 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+$query-spacing-unit: 8px;
+
+.input-card {
+ display: flex;
+ flex-flow: column;
+ padding: 0;
+ margin: 0 auto;
+ margin-top: $query-spacing-unit;
+ height: 250px;
+ width: 95%;
+ overflow: hidden;
+}
+
+.toolbar-icon {
+ padding: 0 14px 0 0;
+ margin: 0;
+}
+
+.spacer {
+ flex: 1 1 auto;
+}
+
+.input-selector {
+ max-height: 42px;
+ min-height: 42px;
+ justify-content: center;
+ //align-items: center;
+ font-size: 0.80rem;
+ font-weight: 500;
+ background-color: white;
+ border: 1px solid rgba(54, 147, 209, 0.87);
+}
+
+.content-area {
+ position: relative;
+ color: hsla(0,0%,0%,.87);
+ height: 120px;
+ padding: 0;
+ margin: $query-spacing-unit * 2;
+ overflow: none;
+}
+
+.codemirror-container {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ font-size: 14px;
+ line-height: 1.8;
+}
+
+.actions {
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+ color: rgba(54, 147, 209, 0.87);
+ padding: $query-spacing-unit;
+ margin: 0;
+}
+
+.error-message {
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+ color: rgba(209, 54, 54, 0.87);
+ padding-top: 10px;
+ padding-left: 20px;
+ text-overflow: ellipsis;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/input-metadata.component.ts
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/input-metadata.component.ts b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/input-metadata.component.ts
new file mode 100755
index 0000000..06b9375
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/input-metadata.component.ts
@@ -0,0 +1,111 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+import { Component, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import { Store } from '@ngrx/store';
+import * as sqlQueryActions from '../../shared/actions/query.actions'
+import * as dataverseActions from '../../shared/actions/dataverse.actions'
+import * as datasetActions from '../../shared/actions/dataset.actions'
+import * as datatypeActions from '../../shared/actions/datatype.actions'
+import * as indexActions from '../../shared/actions/index.actions'
+
+
+import * as CodeMirror from 'codemirror';
+
+/*
+ * Query metadata component
+ * has editor (codemirror) for writing some query
+ */
+@Component({
+ moduleId: module.id,
+ selector: 'awc-query-metadata',
+ templateUrl:'input-metadata.component.html',
+ styleUrls: ['input-metadata.component.scss'],
+})
+
+export class InputQueryMetadataComponent {
+ private dataverses$: Observable<any>;
+ private datatypes$: Observable<any>;
+ private datasets$: Observable<any>;
+ private indexes$: Observable<any>;
+ dataverses = [];
+ datatypes = [];
+ datasets = [];
+ indexes = [];
+ queryMetadataString: string = "";
+ loaded$: Observable<any>;
+ errorMessage: string = "";
+
+ /**
+ * Initialize codemirror
+ */
+ codemirrorMetadataConfig = { mode: "asterix",
+ //lineNumbers: true,
+ lineWrapping: true,
+ showCursorWhenSelecting: true,
+ autofocus: true
+ };
+
+ constructor(private store: Store<any>, private changeDetector: ChangeDetectorRef) {
+ this.store.select("sqlMetadataQuery").subscribe((data: any) => {
+ if (data.success === false){
+ if (data.sqlQueryMetadataError.errors){
+ this.errorMessage = "ERROR: " + data.sqlQueryMetadataError.errors[0].msg
+ this.changeDetector.detectChanges();
+ }
+ } else {
+ this.errorMessage = "SUCCEED";
+
+ // Refresh the tables automatically
+ let stringQuery = data.sqlQueryMetadataString;
+ stringQuery = stringQuery.toUpperCase();
+
+ if (stringQuery.includes("CREATE DATAVERSE") || stringQuery.includes("DROP DATAVERSE") ){
+ this.store.dispatch(new dataverseActions.SelectDataverses('-'));
+ }
+ else if (stringQuery.includes("CREATE DATASET") || stringQuery.includes("DROP DATASET")){
+ this.store.dispatch(new datasetActions.SelectDatasets('-'));
+ }
+ else if (stringQuery.includes("CREATE TYPE") || stringQuery.includes("DROP TYPE")){
+ this.store.dispatch(new datatypeActions.SelectDatatypes('-'));
+ }
+ else if (stringQuery.includes("CREATE INDEX") || stringQuery.includes("DROP INDEX")){
+ this.store.dispatch(new indexActions.SelectIndexes('-'));
+ }
+
+ this.changeDetector.detectChanges();
+ }
+
+ })
+ }
+
+ getQueryResults(queryMetadataString: string) {
+ this.store.dispatch(new sqlQueryActions.ExecuteMetadataQuery(queryMetadataString));
+ }
+
+ executeQuery() {
+ this.getQueryResults(this.queryMetadataString.replace(/\n/g, " "));
+ // Component View Refresh
+
+ }
+
+ onClick() {
+ this.errorMessage = "";
+ }
+
+ /* Cleans up error message */
+ cleanUp() {
+ this.errorMessage = "";
+ }
+}
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/metadata-container.component.html
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/metadata-container.component.html b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/metadata-container.component.html
new file mode 100755
index 0000000..a2f3a73
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/metadata-container.component.html
@@ -0,0 +1,36 @@
+<!--/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/ -->
+<mat-tab-group class="metadata-menu" (selectedTabChange)="tabChange()">
+ <mat-tab label="DATAVERSES" class="submenu">
+ <div class="dataverses">
+ <awc-dataverses #dataverses [message]="message" class="dataverses"></awc-dataverses>
+ </div>
+ </mat-tab>
+ <mat-tab label="DATASETS">
+ <div class="datasets">
+ <awc-datasets #datasets [message]="message" class="datasets"></awc-datasets>
+ </div>
+ </mat-tab>
+ <mat-tab label="DATATYPES">
+ <div class="datatypes">
+ <awc-datatypes #datatypes [message]="message" class="datatypes"></awc-datatypes>
+ </div>
+ </mat-tab>
+ <mat-tab label="INDEXES" class="indexes">
+ <div class="indexes">
+ <awc-indexes #indexes [message]="message" class="indexes"></awc-indexes>
+ </div>
+ </mat-tab>
+</mat-tab-group>
+
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/metadata-container.component.scss
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/metadata-container.component.scss b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/metadata-container.component.scss
new file mode 100755
index 0000000..3857d74
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/metadata-container.component.scss
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ .dataverses {
+ margin: 0;
+ min-height: 750px;
+ max-height: 750px;
+ width: 100%;
+ overflow: hidden;
+ }
+
+.datasets {
+ margin: 0;
+ min-height: 750px;
+ max-height: 750px;
+ width: 100%;
+ overflow: hidden;
+}
+
+.datatypes {
+ margin: 0;
+ min-height: 750px;
+ max-height: 750px;
+ width: 100%;
+ overflow: hidden;
+}
+
+.indexes {
+ margin: 0;
+ min-height: 750px;
+ max-height: 750px;
+ width: 100%;
+ overflow: hidden;
+}
+
+.metadata-menu {
+ /deep/ .mat-tab-label {
+ font-size: 0.80rem !important;
+ font-weight: 500 !important;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/metadata-container.component.ts
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/metadata-container.component.ts b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/metadata-container.component.ts
new file mode 100755
index 0000000..c8382cf
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/metadata/metadata-container.component.ts
@@ -0,0 +1,41 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+import { Component, ViewChild } from '@angular/core';
+
+@Component({
+ moduleId: module.id,
+ selector: 'awc-metadata-container',
+ templateUrl: 'metadata-container.component.html',
+ styleUrls: ['metadata-container.component.scss']
+})
+
+export class MetadataContainerComponent {
+
+ @ViewChild('dataverses') dataverses ;
+ @ViewChild('datasets') datasets ;
+ @ViewChild('datatypes') datatypes ;
+ @ViewChild('indexes') indexes ;
+ message = "";
+
+ constructor() {}
+
+ tabChange() {
+ this.indexes.cleanUp();
+ this.datasets.cleanUp();
+ this.datatypes.cleanUp();
+ this.dataverses.cleanUp();
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/codemirror.component.scss
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/codemirror.component.scss b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/codemirror.component.scss
new file mode 100755
index 0000000..ba795c2
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/codemirror.component.scss
@@ -0,0 +1,23 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+/* -- Place holder for future expansion --> */
+code {
+ width: 100%;
+ height: 100%;
+ padding: 10%;
+ margin: 0;
+ overflow-wrap: break-word;
+ word-break: break-all;
+ background-color: pink;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/codemirror.component.ts
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/codemirror.component.ts b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/codemirror.component.ts
new file mode 100755
index 0000000..91b711d
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/codemirror.component.ts
@@ -0,0 +1,237 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+/**
+ * Integrating codemirror (using ng2-codemirror) with our application
+ *
+ * component from "https://runkit.com/npm/ng2-codemirror"
+ * "https://www.npmjs.com/package/ng2-codemirror"
+ * copy component from /src/codemirror.component.ts
+ * and modified for custom mode (asterix aql, sql++ code hilighting)
+ *
+ * therefore, actually we don't need to "npm install ng2-codemirror"
+ *
+ * Because on the outside of this component,
+ * It was hard to access the codemirror instance that 'ng-codemirror' use
+ * So, we copied the component in our application and modified it
+ *
+ * 'codemirror.js(^5.23.0)' is included in the 'index.html'
+ * And in this component(codemirror.component.ts)
+ * add statement like "declare var CodeMirror: any;"
+ *
+ * I don't know whether this is right way
+ *
+ * ref 1) usage : https://embed.plnkr.co/8e9gxss9u10VeFrv29Zt/
+ * ref 2) custom mode : http://jsfiddle.net/TcqAf/99/
+ * ref 3) integrating : http://stackoverflow.com/questions/37092142/integrating-codemirror-with-angular2-typescript
+ * ref 3) integrating : https://medium.com/@s_eschweiler/using-external-libraries-with-angular-2-87e06db8e5d1#.8ok74uvwg
+ */
+ import {
+ Component,
+ Input,
+ Output,
+ ElementRef,
+ ViewChild,
+ EventEmitter,
+ forwardRef,
+ AfterViewInit,
+ OnDestroy
+ } from '@angular/core';
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
+ import * as CodeMirror from 'codemirror';
+
+/**
+ * CodeMirror component
+ * Usage :
+ * <codemirror [(ngModel)]="data" [config]="{...}"></codemirror>
+ */
+@Component({
+ moduleId: module.id,
+ selector: 'codemirror',
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => CodemirrorComponent),
+ multi: true
+ }
+ ],
+ styleUrls: ['codemirror.component.scss'],
+ template: `<textarea class="code" #host></textarea>`,//,
+})
+
+export class CodemirrorComponent implements AfterViewInit, OnDestroy {
+ @Input() config;
+ @Output() change = new EventEmitter();
+ @Output() focus = new EventEmitter();
+ @Output() blur = new EventEmitter();
+ @Output() instance = null;
+ @ViewChild('host') host;
+ _value = '';
+
+ /**
+ * Constructor
+ */
+ constructor(){
+ /**
+ * Custom mode for AsterixDB
+ */
+ CodeMirror.defineMode("asterix", function(){
+ var KEYWORD_MATCH = [
+ // AQL
+ "drop", "dataverse", "dataset",
+ "if", "exists", "create",
+ "use", "type", "as", "closed",
+ "primary", "key", "hints", "cardinality",
+ "index", "on", "btree", "rtree", "keyword",
+ "for", "in", "Metadata", "Dataset",
+ "return", "Index", "load", "using", "localfs", "path", "format",
+ // Query (not perfect)
+ "from", "in", "with", "group", "by", "select",
+ "let", "where", "order", "asc", "desc", "limit",
+ "keeping", "offset", "distinct", "or", "and",
+ // Built in functions (TODO)
+ // Built in functions (TODO)
+ // Built in functions (TODO)
+ // Asterix Data Model
+ // Primitive type
+ "boolean",
+ "tinyint", "smallint", "integer", "bigint",
+ "float", "double",
+ "string",
+ "binary", "hex", "base64",
+ "point", "line", "rectangle", "circle", "polygon",
+ "date", "time", "datetime", "duration", "interval", "uuid",
+ // Incomplete information type
+ "null", "missing",
+ // Derived type
+ // object {}, array [], multiset {{}}
+ // SQL++
+ "DROP", "DATAVERSE", "IF", "EXISTS", "CREATE", "USE", "TYPE", "AS", "DATASET", "PRIMARY", "KEY",
+ "INDEX", "SELECT", "VALUE", "INSERT", "INTO", "FROM", "WHERE", "AND", "SOME", "IN", "SATISFIES", "IS", "UNKNOWN", "NOT", "EVERY",
+ "GROUP", "BY", "ORDER", "DESC", "LIMIT", "OR", "SET", "DELETE", "LOAD", "USING",
+ ];
+
+ //"(", ")","{{", "}}", "[", "]", "{", "}", ";", ",", ":","?", "=",
+ var VAR_MATCH = /[$][a-zA-Z]+(\d*)/;
+ var DOT_MATCH = /[.](\S)*/;
+ var DOUBLE_QUOTE_MATCH = /["].*["]/;
+ var SINGLE_QUOTE_MATCH = /['].*[']/;
+ var BREAK_POINT = /(\s)/;
+
+ return {
+ startState: function() {return {inString: false};},
+ token: function(stream, state) {
+ if (state.newLine == undefined)state.newLine = true;
+
+ //match variable reference
+ if (stream.match(VAR_MATCH)) {
+ return "variable";
+ }
+
+ if (stream.match(DOT_MATCH)) {
+ return "dot-variable";
+ }
+
+ //string variable match
+ if (stream.match(DOUBLE_QUOTE_MATCH)) {
+ return "string";
+ }
+ if (stream.match(SINGLE_QUOTE_MATCH)) {
+ return "string";
+ }
+
+ //keyword match
+ for (var i in KEYWORD_MATCH){
+ if (state.newLine && stream.match(KEYWORD_MATCH[i])){
+ return "keyword";
+ }
+ }
+
+ if (stream.peek() === " " || stream.peek() === null){
+ state.newLine = true;
+ }else{
+ state.newLine = false;
+ }
+ stream.next();
+ return null;
+ }
+ };
+ });
+ }
+
+ get value() { return this._value; };
+
+ @Input() set value(v) {
+ if (v !== this._value) {
+ this._value = v;
+ this.onChange(v);
+ }
+ }
+
+ /**
+ * On component destroy
+ */
+ ngOnDestroy() {}
+
+ /**
+ * On component view init
+ */
+ ngAfterViewInit() {
+ this.config = this.config || {};
+ this.codemirrorInit(this.config);
+ }
+
+ /**
+ * Initialize codemirror
+ */
+ codemirrorInit(config){
+ this.instance = CodeMirror.fromTextArea(this.host.nativeElement, config);
+ this.instance.setValue(this._value);
+ this.instance.setSize(null, 90);
+ this.instance.on('change', () => {
+ this.updateValue(this.instance.getValue());
+ });
+
+ this.instance.on('focus', () => {
+ this.focus.emit();
+ });
+
+ this.instance.on('blur', () => {
+ this.blur.emit();
+ });
+ }
+
+ /**
+ * Value update process
+ */
+ updateValue(value){
+ this.value = value;
+ this.onTouched();
+ this.change.emit(value);
+ }
+
+ /**
+ * Implements ControlValueAccessor
+ */
+ writeValue(value){
+ this._value = value || '';
+ if (this.instance) {
+ this.instance.setValue(this._value);
+ }
+ }
+
+ onChange(_) {}
+ onTouched() {}
+ registerOnChange(fn){this.onChange = fn;}
+ registerOnTouched(fn){this.onTouched = fn;}
+}
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/input.component.html
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/input.component.html b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/input.component.html
new file mode 100755
index 0000000..2eec6b7
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/input.component.html
@@ -0,0 +1,28 @@
+<!--/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/ -->
+<mat-card class="input-card">
+ <mat-toolbar color="primary" class="input-selector">
+ <mat-icon class="toolbar-icon">menu</mat-icon>
+ <span>INPUT: SQL++</span>
+ <span class="spacer"></span>
+ </mat-toolbar>
+ <mat-card-content class="content-area">
+ <div class="codemirror-container">
+ <codemirror class="code" #host [(ngModel)]="queryString" [config]="codemirrorConfig"></codemirror>
+ </div>
+ </mat-card-content>
+ <mat-card-actions class="actions">
+ <button mat-button class="query-button" (click)="onClick()">RUN</button>
+ </mat-card-actions>
+</mat-card>
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/input.component.scss
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/input.component.scss b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/input.component.scss
new file mode 100755
index 0000000..437ff58
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/input.component.scss
@@ -0,0 +1,82 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+$query-spacing-unit: 5px;
+
+.input-card {
+ display: flex;
+ flex-flow: column;
+ padding: 0;
+ margin: ($query-spacing-unit * 2);
+ height: 200px;
+ width: 100%;
+ min-height: 150px;
+
+ //background-color: orange;
+}
+
+.toolbar-icon {
+ padding: 0 14px 0 0;
+ margin: 0;
+}
+
+.spacer {
+ flex: 1 1 auto;
+}
+
+.input-selector {
+ max-height: 42px;
+ min-height: 42px;
+ justify-content: center;
+ //align-items: center;
+ font-size: 0.80rem;
+ font-weight: 500;
+ background-color: white;
+ border: 1px solid rgba(54, 147, 209, 0.87);
+}
+
+.content-area {
+ //position: relative;
+ color: hsla(0,0%,0%,.87);
+ //height: 102px;
+ padding: 0;
+ margin: 0;
+ overflow: none;
+ }
+
+.codemirror-container {
+ width: 95%;
+ height: 98%;
+ padding: 0; // ($query-spacing-unit * 2);
+ margin: 0 auto;
+ font-size: 14px;
+ //letter-spacing: 3px;
+ line-height: 1.8;
+ background-color: red;
+}
+
+//.code {
+// width: 100%;
+// height: 100%;
+// padding: 0;
+// margin: 0;
+// overflow-wrap: break-word;
+// word-break: break-all;
+//}
+
+.actions {
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+ color: rgba(54, 147, 209, 0.87);
+ padding-left: $query-spacing-unit;
+ margin: 0;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/input.component.ts
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/input.component.ts b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/input.component.ts
new file mode 100755
index 0000000..9be9bd9
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/input.component.ts
@@ -0,0 +1,90 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+import { Component, ViewChild } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import { Store } from '@ngrx/store';
+import * as sqlQueryActions from '../../shared/actions/query.actions'
+import * as CodeMirror from 'codemirror';
+
+/*
+ * query component
+ * has editor (codemirror) for writing some query
+ */
+@Component({
+ moduleId: module.id,
+ selector: 'awc-query',
+ templateUrl:'input.component.html',
+ styleUrls: ['input.component.scss']
+})
+
+export class InputQueryComponent {
+ private guideSelectedDataset$: Observable<any>;
+ private dataverses$: Observable<any>;
+ private datatypes$: Observable<any>;
+ private datasets$: Observable<any>;
+ private indexes$: Observable<any>;
+ dataverses = [];
+ datatypes = [];
+ datasets = [];
+ indexes = [];
+ datasetName = "";
+ dataverseName = "";
+ queryString: string = ""
+
+ /* Codemirror configuration
+ */
+ codemirrorConfig = { mode: "asterix",
+ lineWrapping: true,
+ showCursorWhenSelecting: true,
+ autofocus: true
+ } ;
+
+ loaded$: Observable<any>
+
+ constructor(private store: Store<any>) {
+ // Watching for guide selected or clicked dataset
+ this.guideSelectedDataset$ = this.store.select(s => s.dataset.guideSelectsDataset);
+ this.guideSelectedDataset$.subscribe((data: any) => {
+ if (data) {
+ this.datasetName = data;
+ for (let i = 0; i < this.datasets.length; i++) {
+ if ( this.datasets[i]['DatasetName'] === this.datasetName ) {
+ this.dataverseName = this.datasets[i]['DataverseName'];
+ }
+ }
+ this.queryString = "USE " + this.dataverseName + "; SELECT * FROM " + this.datasetName;
+ }
+ });
+
+ // Watching for Datatypes
+ this.dataverses$ = this.store.select(s => s.dataverse.dataverses.results);
+ this.dataverses$.subscribe((data: any[]) => {
+ this.dataverses = data;
+ });
+
+ // Watching for Datasets
+ this.datasets$ = this.store.select(s => s.dataset.datasets.results);
+ this.datasets$.subscribe((data: any[]) => {
+ this.datasets = data;
+ });
+ }
+
+ getQueryResults(queryString: string) {
+ this.store.dispatch(new sqlQueryActions.ExecuteQuery(queryString));
+ }
+
+ onClick() {
+ this.getQueryResults(this.queryString.replace(/\n/g, " "));
+ }
+}
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/metadata.component.html
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/metadata.component.html b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/metadata.component.html
new file mode 100755
index 0000000..4641426
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/metadata.component.html
@@ -0,0 +1,44 @@
+<!--/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/ -->
+<mat-card class="metadata-card">
+ <mat-toolbar color="primary" class="metadata-selector">
+ <mat-icon class="example-icon">menu</mat-icon>
+ <span>METADATA GUIDE</span>
+ <span class="spacer"></span>
+ </mat-toolbar>
+ <div class="metadata-content-area">
+ <div class="metadata-tree">
+ <div class="metadata-all">
+ <p-tree [style]="{'width':'100%', 'border': 'none', 'font-family': 'Roboto Mono', 'font-size': '0.80rem',
+ 'font-weight': '500'}" selectionMode="single" [value]="nodesAll" (onNodeSelect)="nodeSelectAll($event)"></p-tree>
+ </div>
+ <div class="metadata-datasets">
+ <p-tree [style]="{'width':'100%', 'border': 'none', 'font-family': 'Roboto Mono', 'font-size': '0.80rem',
+ 'font-weight': '500'}" selectionMode="single" [value]="nodesDatasets" (onNodeSelect)="nodeSelectDataset($event)"></p-tree>
+ </div>
+ <div class="metadata-datatypes">
+ <p-tree [style]="{'width':'100%', 'border': 'none', 'font-family': 'Roboto Mono', 'font-size': '0.80rem',
+ 'font-weight': '500'}" selectionMode="single" [value]="nodesDatatypes"></p-tree>
+ </div>
+ <div class="metadata-index">
+ <p-tree [style]="{'width':'100%', 'border': 'none', 'font-family': 'Roboto Mono', 'font-size': '0.80rem',
+ 'font-weight': '500'}" selectionMode="single" [value]="nodesIndexes"></p-tree>
+ </div>
+
+ </div>
+ </div>
+ <!--<mat-card-actions class="actions">
+ <button mat-button class="refresh-button" (click)="menuRefresh()">COLLAPSE</button>
+ </mat-card-actions> -->
+</mat-card>
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/metadata.component.scss
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/metadata.component.scss b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/metadata.component.scss
new file mode 100755
index 0000000..4ee2339
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/metadata.component.scss
@@ -0,0 +1,97 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+$metadata-spacing-unit: 5px;
+
+.metadata-card {
+ display: flex;
+ flex-flow: column;
+ padding: 0;
+ margin: 0 auto; //($metadata-spacing-unit * 2);
+ margin-top: ($metadata-spacing-unit * 2);
+ margin-bottom: ($metadata-spacing-unit * 2);
+ min-height: 150px;
+ box-shadow: none !important;
+ width: 92%;
+ overflow: hidden;
+}
+
+.example-icon {
+ padding: 0 14px 0 0;
+ margin: 0;
+}
+
+.spacer {
+ flex: 1 1 auto;
+}
+
+.metadata-selector {
+ min-height: 42px;
+ max-height: 42px;
+ justify-content: center;
+ //align-items: center;
+ font-size: 0.80rem;
+ font-weight: 500;
+ background-color: white;
+ border: 1px solid rgba(54, 147, 209, 0.87);
+}
+
+.metadata-content-area {
+ padding: ($metadata-spacing-unit * 2);
+ margin: 0;
+}
+
+.metadata-tree {
+ min-height: 30px;
+ font-size: 0.80rem;
+ font-weight: 500;
+}
+
+.metadata-datasets {
+ margin-top: ($metadata-spacing-unit * 2);
+ margin-bottom: ($metadata-spacing-unit * 2);
+}
+
+.metadata-datatypes {
+ margin-top: ($metadata-spacing-unit * 2);
+ margin-bottom: ($metadata-spacing-unit * 2);
+}
+
+.metadata-dataindexes {
+ margin-top: ($metadata-spacing-unit * 2);
+ margin-bottom: ($metadata-spacing-unit * 2);
+}
+
+
+.metadata-tree.ui-tree {
+ //width: 260px !important;
+ font-size: 0.80rem;
+ font-weight: 500;
+ border: none !important;
+ background-color: red;
+}
+
+.refresh-button {
+ float: left;
+ margin-top: $metadata-spacing-unit;
+}
+
+.actions {
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+ color: rgba(54, 147, 209, 0.87);
+ padding: $metadata-spacing-unit;
+ margin: 0;
+}
+
+
+
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/metadata.component.ts
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/metadata.component.ts b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/metadata.component.ts
new file mode 100755
index 0000000..e60c9de
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/metadata.component.ts
@@ -0,0 +1,209 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
+import { Router } from '@angular/router';
+import { Dataverse } from '../../shared/models/asterixDB.model';
+import { Store } from '@ngrx/store';
+import { Observable } from 'rxjs/Observable';
+import * as dataverseActions from '../../shared/actions/dataverse.actions';
+import * as datasetActions from '../../shared/actions/dataset.actions';
+import * as datatypesActions from '../../shared/actions/datatype.actions';
+import * as indexesActions from '../../shared/actions/index.actions';
+import * as metadataActions from '../../shared/actions/metadata.actions';
+import * as datasetsActions from '../../shared/actions/dataset.actions';
+import { ElementRef, ViewChild} from '@angular/core';
+import {DataSource} from '@angular/cdk/collections';
+import {BehaviorSubject} from 'rxjs/BehaviorSubject';
+import 'rxjs/add/operator/startWith';
+import 'rxjs/add/observable/merge';
+import 'rxjs/add/operator/map';
+import 'rxjs/add/operator/debounceTime';
+import 'rxjs/add/operator/distinctUntilChanged';
+import 'rxjs/add/observable/fromEvent';
+import { Subscription } from 'rxjs/Rx';
+import * as fromRoot from '../../shared/reducers/dataverse.reducer';
+import { State } from '../../shared/reducers/dataverse.reducer';
+import { TreeModule, TreeNode} from 'primeng/primeng';
+
+
+/**
+ * query component
+ * has editor (codemirror) for writing some query
+ */
+@Component({
+ moduleId: module.id,
+ selector: 'awc-metadata',
+ templateUrl: 'metadata.component.html',
+ styleUrls: ['metadata.component.scss']
+})
+
+export class MetadataComponent implements OnInit {
+ nodesAll = [];
+ nodesDatasets = [];
+ nodesDatatypes = [];
+ nodesIndexes = [];
+
+ constructor(private store: Store<any>, private changeDetector: ChangeDetectorRef) {}
+
+ ngOnInit(): void {
+
+ // Watching for the metadata tree
+ this.store.select(s => s.metadata.tree).subscribe((data: any[]) => {
+
+ this.nodesAll = [];
+ this.nodesDatasets = [];
+ this.nodesDatatypes = [];
+ this.nodesIndexes = [];
+ const indexesMenu = [];
+ const datatypesMenu = [];
+ const datasetsMenu = [];
+ const dataversesRoot = { label: '', children: []};
+ dataversesRoot.label = 'DATAVERSES';
+ dataversesRoot.children = [];
+
+ for (let i = 0; i < data.length; i++) {
+
+ // Don't want to show metadata system datasets, datatypes or indexes
+ // if (data[i]['DataverseName'] && data[i]['DataverseName'] !== "Metadata" )
+ // {
+ // Counting dataverses to align the menu identifiers
+ const dataverse = { label: '', children: [] };
+ dataverse.label = data[i]['DataverseName'];
+ dataversesRoot.children.push(dataverse);
+
+ // Adding the datasets to correspondent dataverse
+ if (data[i]['Datasets'].length) {
+ const datasetRoot = { label: '', children: [] };
+ datasetRoot.label = 'DATASETS';
+ dataverse.children.push(datasetRoot);
+ for (let j = 0; j < data[i]['Datasets'].length; j++) {
+ const dataset = { label: '', children: [] };
+ dataset.label = data[i]['Datasets'][j]['DatasetName'];
+
+ //
+ // Adding the datatype to correspondent dataset
+ //
+ if (data[i]['Datasets'][j]['Datatype']) {
+ const datatypeRoot = { label: '', children: [] };
+ datatypeRoot.label = 'Datatype: ' + data[i]['Datasets'][j]['Datatype']['DatatypeName'];
+ //
+ // Looking for the datatype fields
+ //
+ if (data[i]['Datasets'][j]['Datatype']['Derived']) {
+ if (data[i]['Datasets'][j]['Datatype']['Derived']['Record']) {
+ const datatypeFieldsRoot = { label: '', leaf: true, expanded: true, children: [] };
+ datatypeFieldsRoot.label = 'FIELDS';
+ for (let k = 0; k < data[i]['Datasets'][j]['Datatype']['Derived']['Record']['Fields'].length; k++) {
+ const datatypeField = { label: '', children: [] };
+ datatypeField.label = data[i]['Datasets'][j]['Datatype']['Derived']['Record']['Fields'][k]['FieldName'] + ": " + data[i]['Datasets'][j]['Datatype']['Derived']['Record']['Fields'][k]['FieldType'];
+ datatypeFieldsRoot.children.push(datatypeField);
+ }
+ datatypeRoot.children.push(datatypeFieldsRoot);
+
+ }
+ }
+ dataset.children.push(datatypeRoot);
+
+ datatypeRoot.label = data[i]['Datasets'][j]['Datatype']['DatatypeName'];
+ datatypesMenu.push(datatypeRoot);
+ }
+
+ //
+ // Adding the indexes to correspondent dataset
+ //
+ if (data[i]['Datasets'][j]['Indexes'].length) {
+ const indexRoot = { label: '', children: [] };
+ indexRoot.label = 'INDEXES';
+
+ for (let k = 0; k < data[i]['Datasets'][j]['Indexes'].length; k++) {
+ const indexChild = { label: '', children: [] };
+ indexChild.label = data[i]['Datasets'][j]['Indexes'][k]['IndexName'];
+
+ // is Primary
+ const indexIsPrimaryRoot = { label: '', children: [] };
+ indexIsPrimaryRoot.label = 'isPrimary' + ': ' + data[i]['Datasets'][j]['Indexes'][k]['IsPrimary'];
+ indexChild.children.push(indexIsPrimaryRoot);
+
+ // SearchKey
+ if (data[i]['Datasets'][j]['Indexes'][k]['SearchKey']) {
+ const indexSearchKeyRoot = { label: '', children: [] };
+ indexSearchKeyRoot.label = 'SEARCH KEY';
+ for (let l = 0; l < data[i]['Datasets'][j]['Indexes'][k]['SearchKey'].length; l++) {
+ const indexsearchKeyField = { label: '', children: [] };
+ indexsearchKeyField.label = data[i]['Datasets'][j]['Indexes'][k]['SearchKey'][l]
+ indexSearchKeyRoot.children.push(indexsearchKeyField);
+ }
+
+ indexChild.children.push(indexSearchKeyRoot);
+ indexesMenu.push(indexChild);
+ }
+
+ indexRoot.children.push(indexChild);
+ }
+
+ dataset.children.push(indexRoot);
+ datasetRoot.children.push(dataset);
+ datasetsMenu.push(dataset);
+ }
+ }
+ }
+ // }
+ }
+
+ this.nodesAll.push(dataversesRoot);
+
+ /*
+ * Making the rest of the stand alone submenus
+ */
+
+ // Adding the DATASET stand alone submenu
+ const datasetMenuRoot = { label: '', children: [] };
+ datasetMenuRoot.label = 'DATASETS';
+ datasetMenuRoot.children = datasetsMenu;
+ this.nodesDatasets.push(datasetMenuRoot);
+
+ // Adding the DATATYPES stand alone submenu
+ const datatypeMenuRoot = { label: '', children: [] };
+ datatypeMenuRoot.label = 'DATATYPES';
+ datatypeMenuRoot.children = datatypesMenu;
+ this.nodesDatatypes.push(datatypeMenuRoot);
+
+ // Adding the DATATYPE stand alone submenu
+ const indexesMenuRoot = { label: '', children: [] };
+ indexesMenuRoot.label = 'INDEXES';
+ indexesMenuRoot.children = indexesMenu;
+ this.nodesIndexes.push(indexesMenuRoot);
+
+ // Component View Refresh
+ this.changeDetector.detectChanges();
+ });
+ }
+
+ /*
+ * UI helpers to select dataverses from the guide menu
+ */
+ nodeSelectAll(event) {
+ if (event.node.parent && event.node.parent.label === 'DATASETS') {
+ const datasetName = event.node.label.replace(/-;-/g);
+ this.store.dispatch(new datasetsActions.GuideSelectDatasets(datasetName));
+ }
+ }
+
+ nodeSelectDataset(event) {
+ if (event.node.parent && event.node.parent.label === 'DATASETS') {
+ const datasetName = event.node.label.replace(/-;-/g);
+ this.store.dispatch(new datasetsActions.GuideSelectDatasets(datasetName));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/ouput.component.ts
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/ouput.component.ts b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/ouput.component.ts
new file mode 100755
index 0000000..fcfc235
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/ouput.component.ts
@@ -0,0 +1,278 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+import { Component, OnInit, ViewChild, AfterViewInit, ChangeDetectorRef, Pipe, PipeTransform } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import { Store } from '@ngrx/store';
+import * as sqlQueryActions from '../../shared/actions/query.actions'
+import { saveAs } from 'file-saver';
+import { DomSanitizer } from '@angular/platform-browser';
+import {TreeModule,TreeNode} from 'primeng/primeng';
+
+/**
+ * query component
+ * has editor (codemirror) for writing some query
+ */
+
+@Pipe({ name: 'safeHtml'})
+export class SafeHtmlPipe implements PipeTransform {
+ constructor(private sanitized: DomSanitizer) {}
+ transform(value) {
+ return this.sanitized.bypassSecurityTrustHtml(value);
+ }
+}
+
+@Component({
+ moduleId: module.id,
+ selector: 'awc-results',
+ templateUrl:'output.component.html',
+ styleUrls: ['output.component.scss']
+})
+
+
+export class QueryOutputComponent implements OnInit {
+ queryMessage: string;
+ treeData = [];
+ flattenData = [];
+ dataColumns = [];
+ query_message: string;
+ execution_time: number;
+ loaded$: Observable<any>
+ data: any[];
+ loading: Boolean;
+ jsonOutput = "";
+ selectedOutputView = "NONE";
+ outputQueryString = "";
+ toogleExpand = "EXPAND TREE"
+
+ /* Codemirror configuration */
+ codemirrorConfig = { mode: "asterix",
+ lineWrapping: true,
+ showCursorWhenSelecting: true
+ };
+
+ generateTreeMenu(node, rootMenu): any {
+
+ // Check in case the root object is not defined properly
+ if (rootMenu === undefined) {
+ rootMenu = { label: '', children: []};
+ }
+
+ let nodeArray = [];
+
+ // Going through all the keys in a node looking for objects or array of key values
+ // and create a sub menu if is an object.
+ Object.keys(node).map((k) => {
+
+ if (typeof node[k] === 'object') {
+ let nodeObject = { label: '', children: []};
+ nodeObject = { label: '', children: []};
+ nodeObject.label = k;
+ // if this is an object then a new node is created and
+ // recursive call to find and fill with the nested elements
+ let newNodeObject = this.generateTreeMenu(node[k], nodeObject);
+
+ // if this is the first node, then will become the root.
+ if (rootMenu.children) {
+ rootMenu.children.push(newNodeObject)
+ } else {
+ rootMenu = newNodeObject
+ }
+ }
+ else {
+ // Array of key values converted into a unique string with a : separator
+ let nodeKeyValue = { label: '', children: []};
+ nodeKeyValue.label = k + " : " + node[k]
+ nodeArray.push(nodeKeyValue);
+ }
+ })
+
+ // The array will be added as value to a parent key.
+ if (nodeArray.length > 0) {
+ rootMenu.children = nodeArray.concat(rootMenu.children)
+ }
+
+ return rootMenu
+ }
+
+ constructor(private store: Store<any>, private changeDetector: ChangeDetectorRef) {
+ this.loaded$ = this.store.select(s => s.sqlQuery.loaded);
+ this.store.select("sqlQuery").subscribe((data: any) => {
+ // Set the output toolbar query string and default view settings
+ if (data.loaded) {
+ this.selectedOutputView = "TABLE";
+ this.loading = true;
+ this.data = data.sqlQueryResult.results;
+ this.treeData = [];
+ let stringQuery = data.sqlQueryString;
+
+ // Preparing the toolbar
+ if (stringQuery.length > 150) {
+ this.outputQueryString = ": " + stringQuery.slice(0, 150) + " (..)"
+ } else {
+ this.outputQueryString = ": " + stringQuery;
+ }
+
+ // Processing the results
+ if (data.sqlQueryResult.results && data.sqlQueryResult.results.length > 0 && this.data[0]) {
+
+ /* Removing the root object, disabled for the time being
+ var resultKeyList = Object.keys(this.data[0]);
+ var resultKey: string = resultKeyList[0];
+ */
+
+ for (let i = 0; i < this.data.length; i++) {
+
+ /* Removing the root object, disabled for the time being
+ if (this.data[i][resultKey] instanceof Object) {
+ this.data[i] = this.data[i][resultKey];
+ }*/
+
+ let nodeContent = { label:"[" + i + "]" , children: []};
+ this.treeData.push(this.generateTreeMenu(this.data[i], nodeContent))
+ }
+
+ this.loading = false;
+ }
+
+ // JSON OUTPUT
+ // Making into a JSON String for JSON String Output
+ this.jsonOutput = JSON.stringify(data.sqlQueryResult.results, null, 2)
+
+ // TABLE OUTPUT
+ if (this.data && this.data.length > 0) {
+
+ this.collapseAll();
+ // Normalize the data ( removing the first key if is an object )
+ // TODO: Move it into a recursive function.
+ this.dataColumns = [];
+
+ var resultKeyList = Object.keys(this.data[0]);
+ var resultKey: string = resultKeyList[0];
+ if (this.data[0][resultKey] instanceof Object) {
+ // is a SQL++ Query Results
+ var nestedKeyList = Object.keys(this.data[0][resultKey]);
+ for (let i = 0; i < nestedKeyList.length; i++) {
+ if (typeof this.data[0][resultKey][nestedKeyList[i]] === 'object') {
+ // Creating a key to display a nested type
+ this.dataColumns.push({field: 'nestedString' + i, header: nestedKeyList[i]})
+
+ } else {
+ this.dataColumns.push({field: nestedKeyList[i], header: nestedKeyList[i] })
+ }
+
+ }
+ }
+ else { // is a SQL++ Metadata Results and there is an Array
+ for (let i = 0; i < resultKeyList.length; i++) {
+ this.dataColumns.push({field: resultKeyList[i], header: resultKeyList[i] })
+ }
+ }
+
+ // Now prepare the data ( SQL++ Query, Metatada Queries no need to change anything ).
+ // TODO: Move it into a recursive function.
+ if (this.data[0][resultKey] instanceof Object) {
+ // is a SQL++ Query Results
+ for (let i = 0; i < this.data.length; i++) {
+
+ // // is a SQL++ Query Results
+ var nestedKeyList = Object.keys(this.data[i][resultKey]);
+ for (let k = 0; k < nestedKeyList.length; k++) {
+ if ( typeof this.data[i][resultKey][nestedKeyList[k]] === 'object' ){
+ // Creating a display value to for a nested type JSON.stringify(jsObj,
+ var nestedObjectStr = JSON.stringify(this.data[i][resultKey][nestedKeyList[k]], null, '\n');
+ var nestedKey = 'nestedString' + k;
+ this.data[i][resultKey][nestedKey] = nestedObjectStr;
+ }
+ }
+
+ this.data[i] = this.data[i][resultKey];
+ }
+ }
+ }
+ }
+ });
+ }
+
+ /*
+ * Subscribing to store values
+ */
+ ngOnInit(): void {
+ this.loaded$ = this.store.select('sqlQuery');
+ this.store.select("sqlQuery").subscribe((data: any) => {
+ if (data.sqlQueryError.errors){
+ this.queryMessage = data.sqlQueryError.errors[0].msg
+ }else{
+ this.queryMessage = ""
+ }
+ })
+ }
+
+ /*
+ * Changes view mode [ TABLE, TREE, JSON VIEW ]
+ */
+ onSelect(value: any) {
+ this.selectedOutputView = value;
+ }
+
+ /*
+ * Export to CSV
+ */
+ exportToCSV(){
+ var blob = new Blob([this.jsonOutput], {type: "text/csv;charset=utf-8"});
+ saveAs(blob, "Asterix-results.csv");
+ }
+
+ /*
+ * Export to plain text
+ */
+ exportToText(){
+ var exportOutput = this.jsonOutput;
+ var blob = new Blob([exportOutput], {type: "text/plain;charset=utf-8"});
+ saveAs(blob, "Asterix-results.txt");
+ }
+
+ /*
+ * Expand/Collapse Tree
+ */
+ expandTree(){
+ if (this.toogleExpand === "EXPAND TREE"){
+ this.expandAll();
+ } else {
+ this.collapseAll();
+ }
+ }
+
+ expandAll(){
+ this.toogleExpand = "TREE COLLAPSE";
+ this.treeData.forEach( node => {
+ this.expandRecursive(node, true);
+ } );
+ }
+
+ collapseAll(){
+ this.toogleExpand = "EXPAND TREE";
+ this.treeData.forEach( node => {
+ this.expandRecursive(node, false);
+ } );
+ }
+
+ private expandRecursive(node:TreeNode, isExpand:boolean){
+ node.expanded = isExpand;
+ if(node.children){
+ node.children.forEach( childNode => {
+ this.expandRecursive(childNode, isExpand);
+ } );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/output.component.html
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/output.component.html b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/output.component.html
new file mode 100755
index 0000000..f7c4b43
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/output.component.html
@@ -0,0 +1,68 @@
+<!--/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/ -->
+<mat-card class="sql-results-card">
+ <mat-toolbar color="primary" class="results-selector">
+ <mat-icon class="toolbar-icon">menu</mat-icon>
+ <span class="output-query-string">OUTPUT{{outputQueryString}}</span>
+ <span class="spacer"></span>
+ </mat-toolbar>
+ <mat-card-content class="content-area">
+ <div *ngIf="loaded$ | async as ld">
+ <div *ngIf="selectedOutputView=='TABLE'">
+ <p-dataTable [style]="{'width':'100%', 'overflow':'hidden'}" id='review-table' [responsive]="true" [hidden]="loading" [value]="data" [rows]="20" [paginator]="true" [pageLinks]="3" [rowsPerPageOptions]="[5,10,20, 30, 40, 50]" >
+ <p-column [style]="{'text-align':'left',
+ 'text-overflow': 'ellipsis', 'word-wrap': 'break-word', 'word-break': 'break-all'}"
+
+ [footerStyle]="{'color':'blue'}" [headerStyleClass]="datatable-header" *ngFor="let node of dataColumns;" [field]="node.field"
+ [header]="node.header" [sortable]="true">
+ </p-column>
+ </p-dataTable>
+ </div>
+ </div>
+ <div *ngIf="loaded$ | async as ld">
+ <div *ngIf="ld.sqlQueryError.metrics" class="queryErrorMessage">
+ <span>ERROR:</span>
+ <span>{{queryMessage}}</span>
+ </div>
+ <div [hidden]="selectedOutputView!='TREE'" class="data-viewer-container">
+ <button mat-button class="button-expand" (click)="expandTree()">{{toogleExpand}}</button>
+ <p-tree [style]="{'width':'100%', 'border': 'none', 'font-family': 'Roboto Mono', 'font-size': '0.80rem',
+ 'font-weight': '500'}" [value]="treeData"></p-tree>
+ </div>
+ <div *ngIf="loaded$ | async as ld">
+ <div *ngIf="selectedOutputView=='JSON'" class="data-viewer-container">
+ <button mat-button class="button-export" (click)="exportToText()">EXPORT</button>
+ <pre class="json-output">{{jsonOutput}}</pre>
+ </div>
+ </div>
+ </div>
+ </mat-card-content>
+ <mat-card-actions class="actions">
+ <div *ngIf="loaded$ | async as ld">
+ <span *ngIf="ld.sqlQueryResult.metrics" class="metrics">
+ <span class="span-results">SUCCESS:</span>
+ <span class="span-results">Count: {{ld.sqlQueryResult.metrics.resultCount}}</span>
+ <span class="span-results">Size: {{ld.sqlQueryResult.metrics.resultSize}}</span>
+ <span class="span-results">Elapsed time: {{ld.sqlQueryResult.metrics.elapsedTime}}</span>
+ <span class="span-results">Execution time: {{ld.sqlQueryResult.metrics.executionTime}}</span>
+ <span class="spacer"></span>
+ <mat-button-toggle-group #group="matButtonToggleGroup" class="output-group" value={{selectedOutput}} (change)="onSelect(group.value)">
+ <mat-button-toggle mat-button value="TABLE">TABLE</mat-button-toggle>
+ <mat-button-toggle mat-button value="TREE">TREE</mat-button-toggle>
+ <mat-button-toggle mat-button value="JSON">JSON</mat-button-toggle>
+ </mat-button-toggle-group>
+ </span>
+ </div>
+ </mat-card-actions>
+</mat-card>
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/output.component.scss
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/output.component.scss b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/output.component.scss
new file mode 100755
index 0000000..099ca87
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/output.component.scss
@@ -0,0 +1,169 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+$results-spacing-unit: 5px;
+
+.sql-results-card {
+ display: flex;
+ flex-flow: column;
+ padding: 0;
+ height: 600px;
+ width: 100%; // 1350px;
+ margin: ($results-spacing-unit * 2);
+ min-height: 150px;
+}
+
+.toolbar-icon {
+ padding: 0 14px 0 0;
+ margin: 0;
+}
+
+.spacer {
+ flex: 1 1 auto;
+}
+
+.results-selector {
+ max-height: 42px;
+ min-height: 42px;
+ justify-content: center;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ font-size: 0.80rem;
+ font-weight: 500;
+ background-color: white;
+ border: 1px solid rgba(54, 147, 209, 0.87);
+ overflow-wrap: break-word;
+ word-break: break-all;
+}
+
+.content-area {
+ position: relative;
+ color: hsla(0,0%,0%,.87);
+ height: 500px;
+ padding: 0;
+ margin: 0;
+ overflow: auto;
+ font-size: 0.80rem;
+ font-weight: 500;
+ font-family: "Roboto", monospace;
+}
+
+.root-closed {
+ list-style-type:none;
+}
+
+.root-open {
+ list-style-type:none;
+}
+
+.leaf-list-open {
+ list-style-type:none;
+ // padding-top: ($results-spacing-unit) * 2;
+ padding-left: 25px;
+ color: red;
+}
+
+.leaf-list-open.div
+//.leaf-list-open.ul
+.leaf-list-open.li {
+ margin-left: ($results-spacing-unit * 10) !important;
+ color: green;
+}
+
+.leaf {
+ color: blue;
+}
+
+.leaf-list-closed {
+ list-style-type:none;
+ display: none;
+}
+
+ul > .root-closed::before {
+ content:'+'
+}
+
+ul > .root-open::before {
+ content:'-'
+}
+
+.queryErrorMessage {
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+ color: rgba(209, 54, 54, 0.87);
+ padding: $results-spacing-unit;
+ padding-left: ($results-spacing-unit * 2);
+}
+
+.metrics {
+ display: flex;
+ color: rgba(54, 147, 209, 0.87);
+ font-size: 0.80rem;
+ font-weight: 500;
+}
+
+.span-results {
+ padding-top: ($results-spacing-unit * 2);
+ padding-left: ($results-spacing-unit * 2);
+}
+
+.actions {
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+ color: rgba(54, 147, 209, 0.87);
+ margin: 0;
+}
+
+//someID\:review-table
+th {
+ text-align: left !important;
+}
+
+.datatable-header {
+ color: red !important;
+ background-color: blue;
+}
+
+.data-viewer-container {
+ padding: ($results-spacing-unit * 4);
+ padding-bottom: ($results-spacing-unit * 8);
+ height: 100%;
+ overflow: hidden;
+}
+
+.output-group {
+ margin-right: ($results-spacing-unit * 4);
+}
+
+.menu-export {
+ font-size: 0.80rem !important;
+ font-weight: 500 !important;
+}
+
+.button-export {
+ margin-right: ($results-spacing-unit * 4);
+ color: rgba(54, 147, 209, 0.87);
+}
+
+.button-expand {
+ margin-right: ($results-spacing-unit * 4);
+ color: rgba(54, 147, 209, 0.87);
+}
+
+.ui-datatable-data> tr> td {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 150px;
+ color: red;
+}
+
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/query-container.component.html
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/query-container.component.html b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/query-container.component.html
new file mode 100755
index 0000000..6dd3ef3
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/query-container.component.html
@@ -0,0 +1,24 @@
+<!--/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/ -->
+
+<div class="query-container">
+ <div class="metadata">
+ <awc-metadata class="metadata-card"></awc-metadata>
+ </div>
+ <div class="vertical">
+ <awc-query class="query-card"></awc-query>
+ <awc-results class="output-card"></awc-results>
+ </div>
+</div>
+
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/query-container.component.scss
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/query-container.component.scss b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/query-container.component.scss
new file mode 100755
index 0000000..d6b530b
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/query-container.component.scss
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ .query-container {
+ display: flex;
+ flex-direction: row;
+ //background-color: red;
+ width: 100%;
+ margin:0;
+ padding:0;
+ overflow: hidden;
+ }
+
+.metadata {
+ display: flex;
+ flex-direction: row;
+ width: 20%;
+ // background-color: rgb(0, 255, 42);
+ margin:0;
+ padding: 0;
+ // padding-right: 10px;
+ border-right: 1px solid hsla(0,0%,0%,.20);
+}
+
+.vertical {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 80%;
+ overflow: hidden;
+ margin:0;
+ padding: 1px0;
+ // background-color: rgb(38, 0, 255);
+}
+
+.metadata-card {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ width: 100%;
+ overflow: hidden;
+ margin:0;
+ padding: 0;
+ // background-color: green;
+}
+
+.query-card {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ width: 100%;
+ overflow: hidden;
+ margin:0;
+ padding: 0;
+ //background-color: green;
+}
+
+.output-card {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ width: 100%;
+ overflow: hidden;
+ margin:0;
+ padding: 0;
+ //background-color: yellow;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/query-container.component.ts
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/query-container.component.ts b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/query-container.component.ts
new file mode 100755
index 0000000..776e184
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/dashboard/query/query-container.component.ts
@@ -0,0 +1,74 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { Dataverse } from '../../shared/models/asterixDB.model'
+import { Store } from '@ngrx/store';
+import { Observable } from 'rxjs/Observable';
+import * as dataverseActions from '../../shared/actions/dataverse.actions'
+import * as datasetActions from '../../shared/actions/dataset.actions'
+import * as datatypesActions from '../../shared/actions/datatype.actions'
+import * as indexesActions from '../../shared/actions/index.actions'
+import * as metadataActions from '../../shared/actions/metadata.actions'
+import { ElementRef, ViewChild} from '@angular/core';
+import {DataSource} from '@angular/cdk/collections';
+import {BehaviorSubject} from 'rxjs/BehaviorSubject';
+import 'rxjs/add/operator/startWith';
+import 'rxjs/add/observable/merge';
+import 'rxjs/add/operator/map';
+import 'rxjs/add/operator/debounceTime';
+import 'rxjs/add/operator/distinctUntilChanged';
+import 'rxjs/add/observable/fromEvent';
+import { Subscription } from "rxjs/Rx";
+import * as fromRoot from '../../shared/reducers/dataverse.reducer';
+import { State } from '../../shared/reducers/dataverse.reducer';
+import * as sqlQueryActions from '../../shared/actions/query.actions'
+/*
+ * query component
+ * has editor (codemirror) for writing some query
+ */
+@Component({
+ moduleId: module.id,
+ selector: 'awc-query-container',
+ templateUrl:'query-container.component.html',
+ styleUrls: ['query-container.component.scss']
+})
+
+export class QueryContainerComponent {
+ nodes = []
+ constructor(private store: Store<any>) {
+
+ this.store.select(s => s.metadata.tree).subscribe((data: any[]) => {
+ this.nodes = []
+ for (let i = 0; i < data.length; i++) {
+ if (data[i]['DataverseName']) {
+ let node = { id: 0, name:"", children:[] };
+ node.id = i;
+ node.name = data[i]['DataverseName'];
+ for (let j = 0; j < data[i]['Datasets'].length; j++) {
+ let children = { id: 0, name:"", children:[] };
+ children.id = j
+ children.name = data[i]['Datasets'][j]['DatasetName'];
+ node.children.push(children)
+ }
+ this.nodes.push(node)
+ }
+ }
+ });
+ }
+
+ treeCalc() {
+ this.store.dispatch(new metadataActions.UpdateMetadataTree());
+ }
+}
http://git-wip-us.apache.org/repos/asf/asterixdb/blob/3ae6ef05/asterixdb/asterix-dashboard/src/node/src/app/db.ts
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-dashboard/src/node/src/app/db.ts b/asterixdb/asterix-dashboard/src/node/src/app/db.ts
new file mode 100755
index 0000000..8f51b00
--- /dev/null
+++ b/asterixdb/asterix-dashboard/src/node/src/app/db.ts
@@ -0,0 +1,23 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+import { DBSchema } from '@ngrx/db';
+
+/*
+* Persistent storage capability to the dashboard in case is needed.
+*/
+export const schema: DBSchema = {
+ version: 1,
+ name: 'asterixDB_app',
+ stores: {},
+};