You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ao...@apache.org on 2017/09/28 13:24:51 UTC

[02/50] [abbrv] ambari git commit: AMBARI-22038 Log Search UI: implement application of selected text to service logs filters. (ababiichuk)

AMBARI-22038 Log Search UI: implement application of selected text to service logs filters. (ababiichuk)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/f067ec8c
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/f067ec8c
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/f067ec8c

Branch: refs/heads/branch-3.0-perf
Commit: f067ec8c3aaeb596760db4c2bcb98ce81f75769a
Parents: eead09b
Author: ababiichuk <ab...@hortonworks.com>
Authored: Fri Sep 22 13:19:37 2017 +0300
Committer: ababiichuk <ab...@hortonworks.com>
Committed: Fri Sep 22 15:20:02 2017 +0300

----------------------------------------------------------------------
 .../ambari-logsearch-web/src/app/app.module.ts  |  2 +-
 .../src/app/classes/list-item.class.ts          | 25 ++++++++
 .../dropdown-button.component.ts                |  5 +-
 .../dropdown-list/dropdown-list.component.html  |  5 +-
 .../dropdown-list/dropdown-list.component.ts    |  7 ++-
 .../filter-button/filter-button.component.ts    |  3 +-
 .../filters-panel/filters-panel.component.html  |  3 +-
 .../filters-panel/filters-panel.component.ts    | 10 +++-
 .../logs-list/logs-list.component.html          |  7 ++-
 .../logs-list/logs-list.component.less          |  4 ++
 .../components/logs-list/logs-list.component.ts | 61 +++++++++++++++++++-
 .../menu-button/menu-button.component.spec.ts   | 19 +++++-
 .../menu-button/menu-button.component.ts        |  5 +-
 .../search-box/search-box.component.ts          | 35 ++++++++---
 .../src/app/services/filtering.service.ts       |  5 +-
 .../src/assets/i18n/en.json                     |  4 +-
 .../ambari-logsearch-web/src/polyfills.ts       |  2 +-
 17 files changed, 173 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts b/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
index acf1b9f..fca68b5 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
@@ -84,7 +84,7 @@ export function HttpLoaderFactory(http: Http) {
   return new TranslateHttpLoader(http, 'resources/assets/i18n/', '.json?static=true');
 }
 
-export function getXHRBackend(injector: Injector, browser: BrowserXhr, xsrf: XSRFStrategy, options: ResponseOptions): any {
+export function getXHRBackend(injector: Injector, browser: BrowserXhr, xsrf: XSRFStrategy, options: ResponseOptions): XHRBackend | InMemoryBackendService {
   if (environment.production) {
     return new XHRBackend(browser, options, xsrf);
   } else {

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.class.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.class.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.class.ts
new file mode 100644
index 0000000..adb023b
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.class.ts
@@ -0,0 +1,25 @@
+/**
+ * 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.
+ */
+
+export interface ListItem {
+  id?: string;
+  label?: string;
+  value: any;
+  iconClass?: string;
+  isChecked?: boolean;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts
index 42b9451..43d79f8 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts
@@ -17,6 +17,7 @@
  */
 
 import {Component, OnInit, Input} from '@angular/core';
+import {ListItem} from '@app/classes/list-item.class';
 import {ComponentActionsService} from '@app/services/component-actions.service';
 import {UtilsService} from '@app/services/utils.service';
 
@@ -47,7 +48,7 @@ export class DropdownButtonComponent implements OnInit {
   showSelectedValue: boolean = true;
 
   @Input()
-  options?: any[];
+  options?: ListItem[];
 
   @Input()
   defaultValue?: string;
@@ -82,7 +83,7 @@ export class DropdownButtonComponent implements OnInit {
     this.selectedValue = value;
   }
 
-  updateValue(eventOptions: any): void {
+  updateValue(eventOptions: ListItem): void {
     const value = eventOptions && eventOptions.value,
       action = this.action && this.actions[this.action];
     if (this.isMultipleChoice) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html
index 1baebed..316d3f9 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html
@@ -19,12 +19,15 @@
   <label class="list-item-label" *ngIf="isMultipleChoice">
     <input type="checkbox" [attr.id]="item.id || item.value" [attr.checked]="item.isChecked ? 'checked' : null"
            (change)="changeSelectedItem({value: item.value, isChecked: $event.currentTarget.checked})">
-    <label [attr.for]="item.id || item.value" class="label-container">{{item.label | translate}}
+    <label [attr.for]="item.id || item.value" class="label-container">
+      <span *ngIf="item.iconClass" [ngClass]="item.iconClass"></span>
+      {{item.label | translate}}
       <div #additionalComponent></div>
     </label>
   </label>
   <span class="list-item-label label-container" *ngIf="!isMultipleChoice"
         (click)="changeSelectedItem({value: item.value, label: item.label})">
+    <span *ngIf="item.iconClass" [ngClass]="item.iconClass"></span>
     {{item.label | translate}}
     <div #additionalComponent></div>
   </span>

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts
index 3de664e..82656cf 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts
@@ -17,6 +17,7 @@
  */
 
 import {Component, AfterViewInit, Input, Output, EventEmitter, ViewChildren, ViewContainerRef, QueryList} from '@angular/core';
+import {ListItem} from '@app/classes/list-item.class';
 import {ComponentGeneratorService} from '@app/services/component-generator.service';
 
 @Component({
@@ -37,7 +38,7 @@ export class DropdownListComponent implements AfterViewInit {
   }
 
   @Input()
-  items: any[];
+  items: ListItem[];
 
   @Input()
   defaultAction: Function;
@@ -49,14 +50,14 @@ export class DropdownListComponent implements AfterViewInit {
   additionalLabelComponentSetter?: string;
 
   @Output()
-  selectedItemChange: EventEmitter<any> = new EventEmitter();
+  selectedItemChange: EventEmitter<ListItem> = new EventEmitter();
 
   @ViewChildren('additionalComponent', {
     read: ViewContainerRef
   })
   containers: QueryList<ViewContainerRef>;
 
-  changeSelectedItem(options: any): void {
+  changeSelectedItem(options: ListItem): void {
     this.selectedItemChange.emit(options);
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.ts
index 9940d73..1481583 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.ts
@@ -18,6 +18,7 @@
 
 import {Component, Input, forwardRef} from '@angular/core';
 import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {ListItem} from "@app/classes/list-item.class";
 import {ComponentActionsService} from '@app/services/component-actions.service';
 import {UtilsService} from '@app/services/utils.service';
 import {MenuButtonComponent} from '@app/components/menu-button/menu-button.component';
@@ -56,7 +57,7 @@ export class FilterButtonComponent extends MenuButtonComponent implements Contro
     this.onChange(newValue);
   }
 
-  updateValue(options: any): void {
+  updateValue(options: ListItem): void {
     const value = options && options.value;
     if (this.isMultipleChoice) {
       this.value = this.utils.updateMultiSelectValue(this.value, value, options.isChecked);

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
index dc76e79..99dd43d 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
@@ -21,7 +21,8 @@
                      [defaultLabel]="filters.clusters.defaultLabel" [isMultipleChoice]="true"
                      class="filter-input"></filter-dropdown>
     <search-box formControlName="query" [items]="searchBoxItemsTranslated" class="filter-input"
-                [parameterNameChangeSubject]="queryParameterNameChange"></search-box>
+                [parameterNameChangeSubject]="queryParameterNameChange"
+                [parameterAddSubject]="queryParameterAdd"></search-box>
     <time-range-picker formControlName="timeRange" [defaultLabel]="filters.timeRange.defaultLabel"
                        class="filter-input"></time-range-picker>
     <timezone-picker class="filter-input"></timezone-picker>

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
index 72ab222..42056c6 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
@@ -20,6 +20,8 @@ import {Component} from '@angular/core';
 import {FormGroup} from '@angular/forms';
 import {Subject} from 'rxjs/Subject';
 import {TranslateService} from '@ngx-translate/core';
+import {ListItem} from '@app/classes/list-item.class';
+import {CommonEntry} from '@app/models/common-entry.model';
 import {FilteringService} from '@app/services/filtering.service';
 import {LogsContainerService} from '@app/services/logs-container.service';
 import {AppStateService} from '@app/services/storage/app-state.service';
@@ -67,9 +69,9 @@ export class FiltersPanelComponent {
 
   private logsType: string; // TODO implement setting the parameter depending on user's navigation
 
-  searchBoxItems: any[] = [];
+  searchBoxItems: ListItem[] = [];
 
-  searchBoxItemsTranslated: any[] = [];
+  searchBoxItemsTranslated: CommonEntry[] = [];
 
   get filters(): any {
     return this.filtering.filters;
@@ -83,4 +85,8 @@ export class FiltersPanelComponent {
     return this.filtering.queryParameterNameChange;
   }
 
+  get queryParameterAdd(): Subject<any> {
+    return this.filtering.queryParameterAdd;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
index 29a85c5..2942b20 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
@@ -46,7 +46,10 @@
           <span class="action-icon fa fa-crosshairs"></span>
         </div>
         <div class="log-content-inner-wrapper">
-          <div class="log-content" *ngIf="isColumnDisplayed('log_message')">{{log.log_message}}</div>
+          <div class="log-content" *ngIf="isColumnDisplayed('log_message')"
+               (contextmenu)="openMessageContextMenu($event)">
+            {{log.log_message}}
+          </div>
         </div>
       </div>
       <div *ngFor="let column of displayedColumns">
@@ -56,5 +59,7 @@
     </ng-template>
   </accordion-panel>
 </div>
+<ul #contextmenu data-component="dropdown-list" class="dropdown-menu context-menu" [items]="contextMenuItems"
+    (selectedItemChange)="updateQuery($event)"></ul>
 <pagination class="col-md-12" *ngIf="logs && logs.length" [totalCount]="totalCount" [filtersForm]="filtersForm"
             [filterInstance]="filters.pageSize" [currentCount]="logs.length"></pagination>

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
index 584c1a7..577043f 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
@@ -135,3 +135,7 @@
     }
   }
 }
+
+.context-menu {
+  position: fixed;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
index a081e1b..aeb55da 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import {Component, Input} from '@angular/core';
+import {Component, OnInit, Input, ViewChild, ElementRef} from '@angular/core';
 import {FormGroup} from '@angular/forms';
 import 'rxjs/add/operator/map';
 import {FilteringService} from '@app/services/filtering.service';
@@ -26,11 +26,15 @@ import {UtilsService} from '@app/services/utils.service';
   templateUrl: './logs-list.component.html',
   styleUrls: ['./logs-list.component.less']
 })
-export class LogsListComponent {
+export class LogsListComponent implements OnInit {
 
   constructor(private filtering: FilteringService, private utils: UtilsService) {
   }
 
+  ngOnInit() {
+    this.contextMenuElement = this.contextMenu.nativeElement;
+  }
+
   @Input()
   logs: any[] = [];
 
@@ -40,8 +44,32 @@ export class LogsListComponent {
   @Input()
   displayedColumns: any[] = [];
 
+  @ViewChild('contextmenu', {
+    read: ElementRef
+  })
+  contextMenu: ElementRef;
+
+  private contextMenuElement: HTMLElement;
+
+  private selectedText: string = '';
+
+  private readonly messageFilterParameterName = 'log_message';
+
   readonly customStyledColumns = ['level', 'type', 'logtime', 'log_message'];
 
+  readonly contextMenuItems = [
+    {
+      label: 'logs.addToQuery',
+      iconClass: 'fa fa-search-plus',
+      value: false // 'isExclude' is false
+    },
+    {
+      label: 'logs.excludeFromQuery',
+      iconClass: 'fa fa-search-minus',
+      value: true // 'isExclude' is true
+    }
+  ];
+
   readonly dateFormat: string = 'dddd, MMMM Do';
 
   readonly timeFormat: string = 'h:mm:ss A';
@@ -66,4 +94,33 @@ export class LogsListComponent {
     return this.displayedColumns.some(column => column.name === key);
   }
 
+  openMessageContextMenu(event: MouseEvent): void {
+    const selectedText = getSelection().toString();
+    if (selectedText) {
+      let contextMenuStyle = this.contextMenuElement.style;
+      Object.assign(contextMenuStyle, {
+        left: `${event.clientX}px`,
+        top: `${event.clientY}px`,
+        display: 'block'
+      });
+      this.selectedText = selectedText;
+      document.body.addEventListener('click', this.dismissContextMenu);
+      event.preventDefault();
+    }
+  }
+
+  updateQuery(event: any) {
+    this.filtering.queryParameterAdd.next({
+      name: this.messageFilterParameterName,
+      value: this.selectedText,
+      isExclude: event.value
+    });
+  }
+
+  private dismissContextMenu = (): void => {
+    this.selectedText = '';
+    this.contextMenuElement.style.display = 'none';
+    document.body.removeEventListener('click', this.dismissContextMenu);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
index 65f0ee6..f736fd5 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
@@ -28,6 +28,7 @@ import {HostsService, hosts} from '@app/services/storage/hosts.service';
 import {ComponentActionsService} from '@app/services/component-actions.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {HttpClientService} from '@app/services/http-client.service';
+import {ListItem} from "@app/classes/list-item.class";
 
 import {MenuButtonComponent} from './menu-button.component';
 
@@ -86,7 +87,11 @@ describe('MenuButtonComponent', () => {
         title: 'empty sub-items array'
       },
       {
-        subItems: [{}],
+        subItems: [
+          {
+            value: null
+          }
+        ],
         hasSubItems: true,
         title: 'sub-items present'
       }
@@ -115,13 +120,21 @@ describe('MenuButtonComponent', () => {
         title: 'empty sub-items array'
       },
       {
-        subItems: [{}],
+        subItems: [
+          {
+            value: null
+          }
+        ],
         hideCaret: false,
         hasCaret: true,
         title: 'sub-items present, caret not hidden'
       },
       {
-        subItems: [{}],
+        subItems: [
+          {
+            value: null
+          }
+        ],
         hideCaret: true,
         hasCaret: true,
         title: 'sub-items present, caret hidden'

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
index b674ec6..7e347e6 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
@@ -17,6 +17,7 @@
  */
 
 import {Component, Input, ViewChild, ElementRef} from '@angular/core';
+import {ListItem} from '@app/classes/list-item.class';
 import {ComponentActionsService} from '@app/services/component-actions.service';
 import * as $ from 'jquery';
 
@@ -46,7 +47,7 @@ export class MenuButtonComponent {
   labelClass?: string;
 
   @Input()
-  subItems?: any[];
+  subItems?: ListItem[];
 
   @Input()
   isMultipleChoice: boolean = false;
@@ -90,7 +91,7 @@ export class MenuButtonComponent {
     }
   }
   
-  updateValue(options: any) {
+  updateValue(options: ListItem) {
     // TODO implement value change behaviour
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
index 4730190..e547a62 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
@@ -19,6 +19,7 @@
 import {Component, OnInit, OnDestroy, Input, ViewChild, ElementRef, forwardRef} from '@angular/core';
 import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
 import {Subject} from 'rxjs/Subject';
+import {CommonEntry} from '@app/models/common-entry.model';
 import {UtilsService} from '@app/services/utils.service';
 
 @Component({
@@ -48,6 +49,7 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
     this.parameterInput.addEventListener('blur', this.onParameterInputBlur);
     this.valueInput.addEventListener('blur', this.onValueInputBlur);
     this.parameterNameChangeSubject.subscribe(this.onParameterNameChange);
+    this.parameterAddSubject.subscribe(this.onParameterAdd);
   }
 
   ngOnDestroy() {
@@ -57,6 +59,7 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
     this.parameterInput.removeEventListener('blur', this.onParameterInputBlur);
     this.valueInput.removeEventListener('blur', this.onValueInputBlur);
     this.parameterNameChangeSubject.unsubscribe();
+    this.parameterAddSubject.unsubscribe();
   }
 
   private currentId: number = 0;
@@ -74,22 +77,25 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
   currentValue: string;
 
   @Input()
-  items: any[] = [];
+  items: CommonEntry[] = [];
 
   @Input()
   parameterNameChangeSubject: Subject<any> = this.defaultSubject;
 
+  @Input()
+  parameterAddSubject: Subject<any> = this.defaultSubject;
+
   @ViewChild('parameterInput')
   parameterInputRef: ElementRef;
 
   @ViewChild('valueInput')
   valueInputRef: ElementRef;
 
-  rootElement: HTMLElement;
+  private rootElement: HTMLElement;
 
-  parameterInput: HTMLElement;
+  private parameterInput: HTMLElement;
 
-  valueInput: HTMLElement;
+  private valueInput: HTMLElement;
 
   activeItem?: any;
 
@@ -127,13 +133,17 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
     }
   };
 
+  private getItem(name: string): CommonEntry {
+    return this.items.find(field => field.value === name);
+  }
+
   clear(): void {
     this.isActive = false;
     this.activeItem = null;
     this.currentValue = null;
   }
 
-  itemsListFormatter(item: any): string {
+  itemsListFormatter(item: CommonEntry): string {
     return item.name;
   }
 
@@ -142,8 +152,7 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
   }
 
   onParameterNameChange = (options: any): void => {
-    this.activeItem = typeof options.item === 'string' ?
-      this.items.find(field => field.value === options.item) : options.item;
+    this.activeItem = typeof options.item === 'string' ? this.getItem(options.item) : options.item;
     this.isExclude = options.isExclude;
     this.isActive = true;
     this.isParameterInput = false;
@@ -168,6 +177,18 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
     }
   }
 
+  onParameterAdd = (options: any): void => {
+    const item = this.getItem(options.name);
+    this.parameters.push({
+      id: this.currentId++,
+      name: options.name,
+      label: item.name,
+      value: options.value,
+      isExclude: options.isExclude
+    });
+    this.updateValue();
+  }
+
   removeParameter(event: MouseEvent, id: number): void {
     this.parameters = this.parameters.filter(parameter => parameter.id !== id);
     this.updateValue();

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/app/services/filtering.service.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/filtering.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/filtering.service.ts
index 627dc27..beaf91c 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/filtering.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/filtering.service.ts
@@ -20,6 +20,7 @@ import {Injectable} from '@angular/core';
 import {FormControl, FormGroup} from '@angular/forms';
 import {Subject} from 'rxjs/Subject';
 import * as moment from 'moment-timezone';
+import {ListItem} from '@app/classes/list-item.class';
 import {AppSettingsService} from '@app/services/storage/app-settings.service';
 import {ClustersService} from '@app/services/storage/clusters.service';
 import {ComponentsService} from '@app/services/storage/components.service';
@@ -47,7 +48,7 @@ export class FilteringService {
     });
   }
 
-  private getListItem(name: string): any {
+  private getListItem(name: string): ListItem {
     return {
       label: name,
       value: name
@@ -403,6 +404,8 @@ export class FilteringService {
 
   queryParameterNameChange: Subject<any> = new Subject();
 
+  queryParameterAdd: Subject<any> = new Subject();
+
   loadClusters(): void {
     this.httpClient.get('clusters').subscribe(response => {
       const clusterNames = response.json();

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
index 1235a80..8750d64 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
+++ b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
@@ -139,5 +139,7 @@
   "logs.stack": "Stack",
   "logs.taskId": "Task Id",
   "logs.versionNote": "Version Note",
-  "logs.versionNumber": "Version Number"
+  "logs.versionNumber": "Version Number",
+  "logs.addToQuery": "Add to Query",
+  "logs.excludeFromQuery": "Exclude from Query"
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f067ec8c/ambari-logsearch/ambari-logsearch-web/src/polyfills.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/polyfills.ts b/ambari-logsearch/ambari-logsearch-web/src/polyfills.ts
index 0ab921a..016ab77 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/polyfills.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/polyfills.ts
@@ -42,7 +42,7 @@ import 'core-js/es6/object';
 // import 'core-js/es6/function';
 // import 'core-js/es6/parse-int';
 // import 'core-js/es6/parse-float';
-// import 'core-js/es6/number';
+import 'core-js/es6/number';
 // import 'core-js/es6/math';
 import 'core-js/es6/string';
 // import 'core-js/es6/date';