Project

General

Profile

1
import {Component, Input, ViewChild, Output, EventEmitter} from '@angular/core';
2
import {Observable}       from 'rxjs/Observable';
3
import {Location} from '@angular/common';
4
import { Filter, Value} from './searchHelperClasses.class';
5
import {SearchResult}     from '../../utils/entities/searchResult';
6
import {SearchFields} from '../../utils/properties/searchFields';
7
import {SearchUtilsClass} from './searchUtils.class';
8
import {DOI} from '../../utils/string-utils.class';
9
import {ModalLoading} from '../../utils/modal/loading.component';
10

    
11
@Component({
12
    selector: 'search-page',
13
    template: `
14

    
15
    <div class="uk-margin-top">
16
      <div class="page-header">
17
          <h1>{{pageTitle}}</h1>
18
      </div>
19
      <div>
20
        <div *ngIf="showRefine" class="uk-grid">
21
          <div  class="uk-width-1-1  uk-margin uk-panel uk-panel-box uk-panel-box-default">
22
              <search-form [(keyword)]="searchUtils.keyword" (keywordChange)="keywordChanged($event)"></search-form>
23
              <div *ngIf="isFiltered()" class = " uk-text-center ">
24
                <span *ngIf = "searchUtils.keyword.length > 0">Keywords:<span>{{searchUtils.keyword}}<a (click) = "clearKeywords() "><span class=" clickable" aria-hidden="true">[<i class="uk-icon-remove"></i>]</span></a></span>
25
                </span>
26
                <span *ngFor="let filter of filters " >
27
                  <span *ngIf = "filter.countSelectedValues > 0"> {{filter.title}}:
28
                    <span *ngFor="let value of getSelectedValues(filter); let i = index;  let end = last; " >{{value.name}}<a (click) = "removeFilter(value, filter) "><span class=" e clickable" aria-hidden="true">[<i class="uk-icon-remove"></i>]</span></a>
29
                     <span *ngIf="!end">, </span>
30
                    </span>
31
                  </span>
32
                </span>
33
                <a  (click)="clearFilters()"  class = "btn uk-text-right"> Clear Filters[<i class="uk-icon-remove"></i>]</a>
34
              </div>
35
          </div>
36
          <div  class="uk-grid uk-width-1-1 uk-margin">
37
            <div class=" search-filters uk-width-large-1-4  uk-width-medium-1-4  uk-width-small-1-1">
38
               <search-filter  *ngFor="let filter of filters " [filter]="filter"  [showResultCount]=showResultCount (change)="filterChanged($event)"></search-filter>
39
            </div>
40

    
41
            <div class="uk-width-large-3-4  uk-width-medium-3-4  uk-width-small-1-1" >
42
              <search-paging [type]="type" [(searchUtils)] = "searchUtils"  [(results)] = "results"  [(baseUrl)] = "baseURLWithParameters"></search-paging>
43
              <search-download [type]="csvPath" [csvParams]="csvParams" [totalResults]="searchUtils.totalResults" (downloadClick)="downloadClicked($event)"></search-download>
44
              <search-result [results]="results" [totalResults]="searchUtils.totalResults" [status]=searchUtils.status [page]="searchUtils.page"></search-result>
45
            </div>
46
          </div>
47
        </div>
48

    
49

    
50
        <div *ngIf="!showRefine" >
51
            <search-form [(keyword)]="searchUtils.keyword" (keywordChange)="keywordChanged($event)"></search-form>
52
            <search-paging [type]="type" [(searchUtils)] = "searchUtils"  [(results)] = "results"   [(baseUrl)] = "baseURLWithParameters"></search-paging>
53
            <search-download [type]="csvPath" [csvParams]="csvParams" [totalResults]="searchUtils.totalResults" (downloadClick)="downloadClicked($event)"></search-download>
54
            <search-result [results]="results" [totalResults]="searchUtils.totalResults" [status]=searchUtils.status [page]="searchUtils.page"></search-result>
55
        </div>
56
      </div>
57
    </div>
58

    
59
    <modal-loading [message]= "'Loading results...'"></modal-loading>
60

    
61
    `
62
})
63
export class SearchPageComponent {
64
  @Input() pageTitle = "";
65
  @Input() results = [];
66
  @Input() filters = [];
67
  @Input() type:string = "";
68
  @Input() searchUtils:SearchUtilsClass = new SearchUtilsClass();
69
  @Output() queryChange  = new EventEmitter();
70
  @Output() downloadClick = new EventEmitter();
71
  @Input() baseUrl:string = '';
72
  @Input() showResultCount:boolean = true;
73
  @Input() showRefine:boolean = true;
74
  @Input() refineFields = [];
75
  @Input() csvParams: string;
76
  @Input() csvPath: string;
77
  @ViewChild (ModalLoading) loading : ModalLoading ;
78
  public fieldIdsMap;//:  { [key:string]:{ name:string, operator:string, type:string, indexField:string, equalityOperator:string  }};
79
  private searchFieldsHelper:SearchFields = new SearchFields();
80
  private queryParameters: Map<string, string>  = new Map<string,string>();
81
  private baseURLWithParameters:string = '';
82
  private sub: any;
83
  public countFilters= 0;
84
  constructor (private location: Location ) {
85

    
86
   }
87

    
88
  ngOnInit() {
89
        this.updateBaseUrlWithParameters(this.filters);
90
  }
91
  ngAfterViewChecked(){
92

    
93
  }
94

    
95
  public getQueryParametersFromUrl(params){
96
    //TODO when final search is done-allFqs/fq
97

    
98
    var parameters = "";
99
    var allFqs = "";
100

    
101
    for(var i=0; i< this.refineFields.length ; i++){
102
         var filterId =  this.refineFields[i];
103

    
104
          if(params[filterId] != undefined) {
105
            if(this.queryParameters == undefined){
106
              this.queryParameters = new Map<string,string>();
107
            }
108
             this.queryParameters[filterId]=decodeURIComponent(params[filterId]);
109
             let values = decodeURIComponent(this.queryParameters[filterId]).split(",");
110
             var countvalues = 0;
111
             var fq = "";
112
             for(let value of values) {
113
               countvalues++;
114
               var paramId = this.fieldIdsMap[filterId].param;
115
              parameters+='&' + paramId+ '='+ value;//+"&" + this.fieldIdsMap[paramId].operator + "="+((countvalues == 1)?"and":"or");
116
              fq+=(fq.length > 0 ? " " + "or" + " ":"" ) + filterId +" exact " + this.quote(value) ;
117
             }
118
             if(countvalues > 0){
119
               fq="&fq="+fq;
120
             }
121
             allFqs += fq;
122
          }
123

    
124

    
125
   }
126
   var keyword = params['keyword'];
127
   var doiQuery = "";
128
   var keywordQuery = "";
129
   if((keyword && keyword.length > 0)){
130
     if((this.type == 'publications' ||this.type == 'datasets')){
131
       var DOIs:string[] = DOI.getDOIsFromString(keyword);
132
       var doisParams = "";
133

    
134
       for(var i =0 ;i < DOIs.length; i++){
135
         doisParams+=(doisParams.length > 0?" or ":"")+'((pidclassid exact doi) and (pid exact "'+ DOIs[i]+'"))';
136
       }
137
       if(doisParams.length > 0){
138
         doiQuery += "&q=("+doisParams+")"
139
       }
140
     }else{
141
        keywordQuery += "&q=("+this.quote(keyword) +")"
142

    
143
     }
144
   }
145
   return (doiQuery.length > 0 ? doiQuery:keywordQuery) + allFqs;
146
 }
147
 public getIndexQueryParametersFromUrl(params){
148
   var parameters = "";
149
   var allFqs = "";
150

    
151
   for(var i=0; i< this.refineFields.length ; i++){
152
        var filterId =  this.refineFields[i];
153
        var fq = "";
154
         if(params[filterId] != undefined) {
155
           if(this.queryParameters == undefined){
156
             this.queryParameters = new Map<string,string>();
157
           }
158
            this.queryParameters[filterId]=decodeURIComponent(params[filterId]);
159
            let values = decodeURIComponent(this.queryParameters[filterId]).split(",");
160
            var countvalues = 0
161
            for(let value of values) {
162
              countvalues++;
163
              parameters+= ((countvalues == 1)?" and (":" or ")+ filterId+ '='+ value;
164
              fq+=(fq.length > 0 ? " " + "or" + " ":"" ) + filterId + " exact " + this.quote(value);
165
            }
166
            parameters+= " ) ";
167
            if(countvalues > 0){
168
              fq="&fq="+fq;
169
            }
170
            allFqs += fq;
171
        }
172

    
173
  }
174
  var keyword = params['keyword'];
175
  var doiQuery = "";
176
  var keywordQuery = "";
177
  if((keyword && keyword.length > 0)){
178
    if((this.type == 'publications' ||this.type == 'datasets')){
179
      var DOIs:string[] = DOI.getDOIsFromString(keyword);
180
      var doisParams = "";
181

    
182
      for(var i =0 ;i < DOIs.length; i++){
183
        doisParams+=(doisParams.length > 0?" or ":"")+'((pidclassid exact doi) and (pid exact "'+ DOIs[i]+'"))';
184
      }
185
      if(doisParams.length > 0){
186
        doiQuery += "and ("+doisParams+")"
187
      }
188
    }else{
189
       keywordQuery += "and ("+this.quote(keyword) +")"
190

    
191
    }
192
  }
193
  return (doiQuery.length > 0 ? doiQuery:keywordQuery) + allFqs;
194

    
195
}
196
 /*
197
 * Mark as check the new filters that are selected, when you get them from search
198
 */
199
  public checkSelectedFilters(filters:Filter[]){
200
    this.filters = filters;
201
       for(var i=0; i< filters.length ; i++){
202
            var filter:Filter = filters[i];
203
            filter.countSelectedValues = 0;
204
              if(this.queryParameters[filter.filterId] != undefined) {
205
                let values = decodeURIComponent(this.queryParameters[filter.filterId]).split(",");
206
                    for(let filterValue of filter.values) {
207
                      if(values.indexOf(filterValue.id) > -1) {
208
                            filterValue.selected = true;
209
                            filter.countSelectedValues++;
210
                         }else{
211
                           filterValue.selected = false;
212

    
213
                         }
214
                    }
215
            }else{
216
              for(let filterValue of filter.values) {
217
                 filterValue.selected = false;
218
              }
219
            }
220
        }
221

    
222
        return filters;
223
  }
224
  /*
225
  * Update the url with proper parameters. This is used as base url in Paging Component
226
  */
227
  public updateBaseUrlWithParameters(filters:Filter[]){
228
    this.baseURLWithParameters = this.baseUrl + this.createUrlParameters(filters,false);
229
  }
230
  /*
231
  *
232
  */
233
  private createUrlParameters(filters:Filter[], includePage:boolean){
234
    var allLimits="";//location.search.slice(1);
235
    for (let filter of filters){
236
      var filterLimits="";
237
      if(filter.countSelectedValues > 0){
238
        for (let value of filter.values){
239
          if(value.selected == true){
240
            filterLimits+=((filterLimits.length == 0)?'':',') + encodeURIComponent(value.id);
241
           }
242
        }
243
        this.queryParameters[filter.filterId]=filterLimits;
244
        allLimits+=(allLimits.length==0?"?":"&")+((filterLimits.length == 0 )?'':filter.filterId + '='+ filterLimits) ;
245
      }
246
    }
247
    if(this.searchUtils.keyword.length > 0 ){
248
       allLimits+=(allLimits.length==0?"?":"&")+'keyword=' + this.searchUtils.keyword;
249
    }
250
    if(this.searchUtils.page != 1 && includePage){
251
       allLimits+=((allLimits.length == 0)?'?':'&') + 'page=' + this.searchUtils.page;
252
    }
253
    return allLimits;
254
  }
255
  /*
256
  *
257
  */
258
  private createSearchQueryParameters(filters:Filter[]){
259
    var allFqs = "";
260
    for (let filter of filters){
261
      if(filter.countSelectedValues > 0){
262
        var fq = "";
263
        var count_selected=0;
264
        for (let value of filter.values){
265
          if(value.selected == true){
266
              count_selected++;
267
              fq+=(fq.length > 0 ? " " + filter.filterOperator + " ":"" ) + filter.filterId +  " exact " + this.quote(value.id);
268
           }
269
        }
270
        fq="&fq="+fq;
271
        allFqs += fq;
272
      }
273
    }
274
    var doiQuery = "";
275
    var keywordQuery = "";
276
    if((this.searchUtils.keyword && this.searchUtils.keyword.length > 0)){
277
      if((this.type == 'publications' ||this.type == 'datasets')){
278
        var DOIs:string[] = DOI.getDOIsFromString(this.searchUtils.keyword);
279
        var doisParams = "";
280

    
281
        for(var i =0 ;i < DOIs.length; i++){
282
          doisParams+=(doisParams.length > 0?" or ":"")+'((pidclassid exact doi) and (pid exact "'+ DOIs[i]+'"))';
283
        }
284
        if(doisParams.length > 0){
285
          doiQuery += "&q=("+doisParams+")"
286
        }else{
287
          keywordQuery += "&q=("+this.quote(this.searchUtils.keyword) +")"
288
        }
289
      }else{
290
         keywordQuery += "&q=("+this.quote(this.searchUtils.keyword) +")"
291
      }
292
    }
293

    
294
    return (doiQuery.length > 0 ? doiQuery:keywordQuery) + allFqs;
295

    
296
  }
297
  private createIndexQueryParameters(filters:Filter[]){
298
    var allFqs = "";
299
    for (let filter of filters){
300
      if(filter.countSelectedValues > 0){
301
        var count_selected=0;
302
        var fq = "";
303
        for (let value of filter.values){
304
          if(value.selected == true){
305
              count_selected++;
306
               fq+=(fq.length > 0 ? " " + filter.filterOperator + " ":"" ) + filter.filterId + " exact " + this.quote(value.id);
307
           }
308
        }
309
        if(count_selected > 0){
310
          fq="&fq="+fq;
311
          allFqs += fq;
312
        }
313
      }
314
    }
315
    var doiQuery = "";
316
    var keywordQuery = "";
317
    if((this.searchUtils.keyword && this.searchUtils.keyword.length > 0)){
318
      if((this.type == 'publications' ||this.type == 'datasets')){
319
        var DOIs:string[] = DOI.getDOIsFromString(this.searchUtils.keyword);
320
        var doisParams = "";
321
        for(var i =0 ;i < DOIs.length; i++){
322
          doisParams+=(doisParams.length > 0?" or ":"")+'((pidclassid exact doi) and (pid exact "'+ DOIs[i]+'"))';
323
        }
324
        if(doisParams.length > 0){
325
          doiQuery += " and ("+doisParams+")"
326
        }
327
      }else{
328
         keywordQuery += " and ("+this.quote(this.searchUtils.keyword) +")"
329

    
330
      }
331
    }
332
    return (doiQuery.length > 0 ? doiQuery:keywordQuery) + allFqs;
333

    
334
  }
335
  private isFiltered(){
336
    var filtered=false;
337
    for (let filter of this.filters){
338
       if(filter.countSelectedValues > 0){
339
           filtered = true;
340
           break;
341
       }
342
     }
343
    if(this.searchUtils.keyword.length > 0 ){
344
      filtered = true;
345
    }
346
    return filtered;
347
  }
348
  private clearKeywords(){
349
    if(this.searchUtils.keyword.length > 0 ){
350
      this.searchUtils.keyword ='';
351
    }
352
    this.goTo(1);
353
  }
354
  private clearFilters(){
355
    for (var i =0 ; i <  this.filters.length; i++){
356
         for (var j=0; j <  this.filters[i].countSelectedValues; j++){
357
          if(this.filters[i].values[j].selected){
358
            this.filters[i].values[j].selected = false;
359
           }
360
        this.filters[i].countSelectedValues = 0;
361
      }
362
    }
363
    this.clearKeywords();
364

    
365
  }
366
  private removeFilter(value:Value,filter:Filter){
367
    filter.countSelectedValues--;
368
    if(value.selected == true){
369
      value.selected = false;
370
     }
371
    this.goTo(1);
372

    
373
  }
374
  goTo(page:number = 1){
375
    this.searchUtils.page = page;
376
    // console.info("searchUtils.page goto = "+this.searchUtils.page);
377
    this.queryParameters = new Map<string,string>();
378
    var urlParameters = this.createUrlParameters(this.filters,true);
379
    // console.info("urlParams : "+urlParameters);
380
    this.updateBaseUrlWithParameters(this.filters);
381
    var queryParameters = this.createSearchQueryParameters(this.filters);
382
    // console.info("queryParams : "+queryParameters);
383
    var indexQuery = this.createIndexQueryParameters(this.filters);
384

    
385
    this.location.go(location.pathname,urlParameters);
386

    
387
    this.queryChange.emit({
388
        value: queryParameters,
389
        index:indexQuery
390

    
391
    });
392

    
393
  }
394
  filterChanged($event){
395
       this.goTo(1);
396
  }
397
  keywordChanged($event) {
398
       this.searchUtils.keyword = $event.value;
399
       this.goTo(1);
400
  }
401

    
402
	downloadClicked($event) {
403
		if($event.value == true) {
404
		    var queryParameters = this.createSearchQueryParameters(this.filters);
405

    
406
		    this.downloadClick.emit({
407
		        value: queryParameters
408
		    });
409
		}
410
	}
411

    
412
  /*
413
  * Get A sub-array of this.refineFields array, which contains the ids of the selected filters
414
  */
415
  public getSelectedFilters():string[] {
416
    var selected:string[] = [];
417
    for(var i=0; i <  this.filters.length; i++){
418
      var filter:Filter = this.filters[i];
419
      if(filter.countSelectedValues > 0){
420
          selected.push(filter.filterId);
421
      }
422
    }
423
    return selected;
424
  }
425
  /*
426
  * Get A sub-array of this.refineFields array, which contains the ids of the selected parameters
427
  */
428
  private getSelectedParameters():string[] {
429
    var selected:string[] = [];
430
    var params:string[] = Object.keys(this.queryParameters);
431
    for(var i=0; i <  params.length; i++){
432
       if(this.refineFields.indexOf(params[i]) > -1){
433
          selected.push(params[i]);
434
      }
435
    }
436
    return selected;
437
  }
438
  /*
439
  * Get A sub-array of this.refineFields array, which hides hidden fields (e.g Funding level 0,1,2,..), and contains those that depend on another fields (e.g  Funding level 0 if Funder is selected )
440
  */
441
  public getFields():string[] {
442
    var selected_filters:string[] = this.getSelectedFilters();
443
    if(selected_filters.length == 0){
444
      selected_filters = this.getSelectedParameters();
445
    }
446
    var fields:string[] = [];
447
    for(var i =0 ; i < this.refineFields.length;i++){
448
      var dependentTo = this.searchFieldsHelper.DEPENDENT_FIELDS[this.refineFields[i]];
449

    
450
      //if filter is not marked as hidden OR it is hidden but it is dependent to a field that it IS selected
451
      if(this.searchFieldsHelper.HIDDEN_FIELDS.indexOf(this.refineFields[i]) == -1 || (selected_filters.indexOf(dependentTo) != -1) ){
452
          fields.push(this.refineFields[i]);
453
       }
454
    }
455
    return fields;
456
  }
457
  /*
458
  * Get a query  string of all fields, that want to get from search (e.g. &fields=funderid&fields=projectstartyear&...))
459
  */
460
  public getRefineFieldsQuery():string{
461

    
462
    var fields:string[] = this.getFields();
463
    var fieldsStr = ""
464
    for(var i =0 ; i < fields.length  ;i++){
465
        fieldsStr+="&fields="+fields[i];
466
    }
467
    return "&refine=true"+fieldsStr;
468
  }
469
  private quote(params: string):string {
470
      return encodeURIComponent('"'+params+'"');
471
  }
472
  // for loading
473
  public openLoading(){
474
    if(this.loading){
475
      this.loading.open();
476
    }
477
  }
478
  public closeLoading(){
479
    if(this.loading){
480
      this.loading.close();
481
    }
482
  }
483
  getSelectedValues(filter):any{
484
    var selected = [];
485
    if(filter.countSelectedValues >0){
486
      for (var i=0; i < filter.values.length; i++){
487
        if(filter.values[i].selected){
488
          selected.push(filter.values[i]);
489
        }
490
      }
491
    }
492
    return selected;
493

    
494
  }
495
}
(9-9/12)