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, StringUtils} 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
              <a *ngIf = "advancedSearchLink && advancedSearchLink.length > 0" routerLinkActive="router-link-active" class="uk-float-right" [routerLink]=advancedSearchLink >More search options <i class="uk-icon-angle-double-right"></i></a>
36
          </div>
37
          <div  class="uk-grid uk-width-1-1 uk-margin">
38
            <div class=" search-filters uk-width-large-1-4  uk-width-medium-1-4  uk-width-small-1-1">
39
               <search-filter  *ngFor="let filter of filters " [filter]="filter"  [showResultCount]=showResultCount (change)="filterChanged($event)"></search-filter>
40
            </div>
41

    
42
            <div class="uk-width-large-3-4  uk-width-medium-3-4  uk-width-small-1-1" >
43
              <search-paging [type]="type" [(searchUtils)] = "searchUtils"  [(results)] = "results"  [(baseUrl)] = "baseUrl" [(parameterNames)] = "parameterNames" [(parameterValues)] = "parameterValues" ></search-paging>
44
              <search-download [type]="csvPath" [csvParams]="csvParams" [totalResults]="searchUtils.totalResults" (downloadClick)="downloadClicked($event)"></search-download>
45
              <search-result    [results]="results"
46
                                [totalResults]="searchUtils.totalResults"
47
                                [status]=searchUtils.status
48
                                [page]="searchUtils.page"
49
                                [type]="entityType" [urlParam]="urlParam">
50
              </search-result>
51
            </div>
52
          </div>
53
        </div>
54

    
55

    
56
        <div *ngIf="!showRefine" >
57
            <search-form [(keyword)]="searchUtils.keyword" (keywordChange)="keywordChanged($event)"></search-form>
58
            <search-paging [type]="type" [(searchUtils)] = "searchUtils"  [(results)] = "results"   [(baseUrl)] = "baseUrl"  [(parameterNames)] = "parameterNames" [(parameterValues)] = "parameterValues"></search-paging>
59
            <search-download [type]="csvPath" [csvParams]="csvParams" [totalResults]="searchUtils.totalResults" (downloadClick)="downloadClicked($event)"></search-download>
60
            <search-result  [results]="results"
61
                            [totalResults]="searchUtils.totalResults"
62
                            [status]=searchUtils.status
63
                            [page]="searchUtils.page"
64
                            [type]="entityType" [urlParam]="urlParam">
65
            </search-result>
66
        </div>
67
      </div>
68
    </div>
69

    
70
    <modal-loading [message]= "'Loading results...'"></modal-loading>
71

    
72
    `
73
})
74
export class SearchPageComponent {
75
  @Input() pageTitle = "";
76
  @Input() results = [];
77
  @Input() filters = [];
78
  @Input() type:string = "";
79
  @Input() entityType: string = "";
80
  @Input() searchUtils:SearchUtilsClass = new SearchUtilsClass();
81
  @Output() queryChange  = new EventEmitter();
82
  @Output() downloadClick = new EventEmitter();
83
  @Input() baseUrl:string = '';
84
  @Input() showResultCount:boolean = true;
85
  @Input() showRefine:boolean = true;
86
  @Input() refineFields = [];
87
  @Input() csvParams: string;
88
  @Input() csvPath: string;
89
  @Input() advancedSearchLink: string = "";
90
  @ViewChild (ModalLoading) loading : ModalLoading ;
91
  public fieldIdsMap;//:  { [key:string]:{ name:string, operator:string, type:string, indexField:string, equalityOperator:string  }};
92
  private searchFieldsHelper:SearchFields = new SearchFields();
93
  private queryParameters: Map<string, string>  = new Map<string,string>();
94
  private baseURLWithParameters:string = '';
95
  private sub: any;
96
  public countFilters= 0;
97
  public urlParam: string;
98
  public parameterNames:string[] =[];
99
  public parameterValues:string[] =[];
100
  constructor (private location: Location ) {
101

    
102
   }
103

    
104
  ngOnInit() {
105
        this.updateBaseUrlWithParameters(this.filters);
106
        this.defineUrlParam();
107
        //console.info(this.entityType + "   " + this.urlParam);
108
  }
109
  ngAfterViewChecked(){
110

    
111
  }
112

    
113
  private defineUrlParam() {
114
      if(this.entityType == "publication") {
115
          this.urlParam = "articleId";
116
      } else if(this.entityType == "dataset") {
117
          this.urlParam = "datasetId";
118
      } else if(this.entityType == "project") {
119
          this.urlParam = "projectId";
120
      } else if(this.entityType == "organization") {
121
          this.urlParam = "organizationId";
122
      } else if(this.entityType == "person") {
123
          this.urlParam = "personId";
124
      } else {
125
          this.urlParam = "datasourceId";
126
      }
127
  }
128

    
129
  public getQueryParametersFromUrl(params){
130
    // var parameters = "";
131
    var allFqs = "";
132

    
133
    for(var i=0; i< this.refineFields.length ; i++){
134
         var filterId =  this.refineFields[i];
135

    
136
          if(params[filterId] != undefined) {
137
            if(this.queryParameters == undefined){
138
              this.queryParameters = new Map<string,string>();
139
            }
140
             this.queryParameters[filterId]=StringUtils.URIDecode(params[filterId]);
141
             let values = (StringUtils.URIDecode(this.queryParameters[filterId])).split(/,(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/,-1);
142
             var countvalues = 0;
143
             var fq = "";
144
             for(let value of values) {
145
               countvalues++;
146
               var paramId = this.fieldIdsMap[filterId].param;
147
              // parameters+='&' + paramId+ '='+ value;//+"&" + this.fieldIdsMap[paramId].operator + "="+((countvalues == 1)?"and":"or");
148
              fq+=(fq.length > 0 ? " " + "or" + " ":"" ) + filterId +" exact " +value;// StringUtils.quote(value) ;
149
             }
150
             if(countvalues > 0){
151
               fq="&fq="+fq;
152
             }
153
             allFqs += fq;
154
          }
155

    
156

    
157
   }
158
   var keyword = params['keyword'];
159
   var doiQuery = "";
160
   var keywordQuery = "";
161
   if((keyword && keyword.length > 0)){
162
      if((this.type == 'publications' ||this.type == 'datasets')){
163
        var DOIs:string[] = DOI.getDOIsFromString(keyword);
164
       var doisParams = "";
165

    
166
       for(var i =0 ;i < DOIs.length; i++){
167
         doisParams+=(doisParams.length > 0?"&":"")+'doi="'+ DOIs[i]+'"';
168
       }
169
       if(doisParams.length > 0){
170
         doiQuery += "&"+doisParams;
171
       }else {
172
        keywordQuery += "&q="+StringUtils.URIEncode(keyword);
173
       }
174
     }else{
175
        keywordQuery += "&q="+StringUtils.URIEncode(keyword);
176

    
177
     }
178
   }
179
   return (doiQuery.length > 0 ? doiQuery:keywordQuery) + allFqs;
180
 }
181
 public getIndexQueryParametersFromUrl(params){
182
  //  var parameters = "";
183
   var allFqs = "";
184

    
185
   for(var i=0; i< this.refineFields.length ; i++){
186
        var filterId =  this.refineFields[i];
187
        var fq = "";
188
         if(params[filterId] != undefined) {
189
           if(this.queryParameters == undefined){
190
             this.queryParameters = new Map<string,string>();
191
           }
192
            this.queryParameters[filterId]=decodeURIComponent(params[filterId]);
193
            let values = (decodeURIComponent(this.queryParameters[filterId])).split(/,(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/,-1);
194
            var countvalues = 0
195
            for(let value of values) {
196
              countvalues++;
197
              // parameters+= ((countvalues == 1)?" and (":" or ")+ filterId+ '='+ value;
198
              fq+=(fq.length > 0 ? " " + "or" + " ":"" ) + filterId + " exact " + value;//StringUtils.quote(value);
199
            }
200
            // parameters+= " ) ";
201
            if(countvalues > 0){
202
              fq="&fq="+fq;
203
            }
204
            allFqs += fq;
205
        }
206

    
207
  }
208
  var keyword = params['keyword'];
209
  var doiQuery = "";
210
  var keywordQuery = "";
211
  if((keyword && keyword.length > 0)){
212
    if((this.type == 'publications' ||this.type == 'datasets')){
213
      var DOIs:string[] = DOI.getDOIsFromString(keyword);
214
      var doisParams = "";
215

    
216
      for(var i =0 ;i < DOIs.length; i++){
217
        doisParams+=(doisParams.length > 0?"&":"")+'doi="'+ DOIs[i]+'"';
218
      }
219
      if(doisParams.length > 0){
220
        doiQuery += "&"+doisParams;
221
      }
222
    }else{
223
       keywordQuery += "and ("+StringUtils.quote(StringUtils.URIEncode(keyword)) +")";
224

    
225
    }
226
  }
227
  return (doiQuery.length > 0 ? doiQuery:keywordQuery) + allFqs;
228

    
229
}
230
 /*
231
 * Mark as check the new filters that are selected, when you get them from search
232
 */
233
  public checkSelectedFilters(filters:Filter[]){
234
    this.filters = filters;
235
       for(var i=0; i< filters.length ; i++){
236
            var filter:Filter = filters[i];
237
            filter.countSelectedValues = 0;
238
              if(this.queryParameters[filter.filterId] != undefined) {
239
                let values = (decodeURIComponent(this.queryParameters[filter.filterId])).split(/,(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/,-1);
240
                     for(let filterValue of filter.values) {
241
                       if(values.indexOf(StringUtils.quote(filterValue.id)) > -1) {
242
                            filterValue.selected = true;
243
                            filter.countSelectedValues++;
244
                         }else{
245
                           filterValue.selected = false;
246

    
247
                         }
248
                    }
249
            }else{
250
              for(let filterValue of filter.values) {
251
                 filterValue.selected = false;
252
              }
253
            }
254
        }
255

    
256
        return filters;
257
  }
258
  /*
259
  * Update the url with proper parameters. This is used as base url in Paging Component
260
  */
261
  public updateBaseUrlWithParameters(filters:Filter[]){
262
    this.baseURLWithParameters = this.baseUrl + this.createUrlParameters(filters,false);
263
  }
264
  /*
265
  *
266
  */
267
  private createUrlParameters(filters:Filter[], includePage:boolean){
268
    var allLimits="";//location.search.slice(1);
269
    this.parameterNames.splice(0,this.parameterNames.length);
270
    this.parameterValues.splice(0,this.parameterValues.length);
271

    
272
    for (let filter of filters){
273
      var filterLimits="";
274
      if(filter.countSelectedValues > 0){
275
        for (let value of filter.values){
276
          if(value.selected == true){
277
            filterLimits+=((filterLimits.length == 0)?'':',') +'"'+ StringUtils.URIEncode(value.id)+'"';
278
           }
279
        }
280
        this.queryParameters[filter.filterId]=filterLimits;
281
        if(filterLimits.length > 0){
282
          this.parameterNames.push(filter.filterId);
283
          this.parameterValues.push(filterLimits);
284
        }
285
        allLimits+=(allLimits.length==0?"?":"&")+((filterLimits.length == 0 )?'':filter.filterId + '='+ filterLimits) ;
286
      }
287
    }
288
    if(this.searchUtils.keyword.length > 0 ){
289
       allLimits+=(allLimits.length==0?"?":"&")+'keyword=' + this.searchUtils.keyword;
290
      this.parameterNames.push("keyword");
291
      this.parameterValues.push(this.searchUtils.keyword);
292
     }
293
    if(this.searchUtils.page != 1 && includePage){
294
       allLimits+=((allLimits.length == 0)?'?':'&') + 'page=' + this.searchUtils.page;
295
    }
296

    
297
    return allLimits;
298
  }
299
  /*
300
  *
301
  */
302
  private createSearchQueryParameters(filters:Filter[]){
303
    var allFqs = "";
304
    for (let filter of filters){
305
      if(filter.countSelectedValues > 0){
306
        var fq = "";
307
        var count_selected=0;
308
        for (let value of filter.values){
309
          if(value.selected == true){
310
              count_selected++;
311
              fq+=(fq.length > 0 ? " " + filter.filterOperator + " ":"" ) + filter.filterId +  " exact " + StringUtils.quote(StringUtils.URIEncode(value.id));
312
           }
313
        }
314
        fq="&fq="+fq;
315
        allFqs += fq;
316
      }
317
    }
318
    var doiQuery = "";
319
    var keywordQuery = "";
320
    if((this.searchUtils.keyword && this.searchUtils.keyword.length > 0)){
321
      if((this.type == 'publications' ||this.type == 'datasets')){
322
        var DOIs:string[] = DOI.getDOIsFromString(this.searchUtils.keyword);
323
        var doisParams = "";
324

    
325
        for(var i =0 ;i < DOIs.length; i++){
326
          doisParams+=(doisParams.length > 0?"&":"")+'doi="'+ DOIs[i]+'"';
327
        }
328
        if(doisParams.length > 0){
329
          doiQuery += "&"+doisParams;
330
        }else{
331
          keywordQuery += "&q="+StringUtils.URIEncode(this.searchUtils.keyword);
332
        }
333
      }else{
334
           keywordQuery += "&q="+StringUtils.URIEncode(this.searchUtils.keyword);
335
      }
336
    }
337

    
338
    return (doiQuery.length > 0 ? doiQuery:keywordQuery) + allFqs;
339

    
340
  }
341
  private createIndexQueryParameters(filters:Filter[]){
342
    var allFqs = "";
343
    for (let filter of filters){
344
      if(filter.countSelectedValues > 0){
345
        var count_selected=0;
346
        var fq = "";
347
        for (let value of filter.values){
348
          if(value.selected == true){
349
              count_selected++;
350
               fq+=(fq.length > 0 ? " " + filter.filterOperator + " ":"" ) + filter.filterId + " exact " + StringUtils.quote(StringUtils.URIEncode(value.id));
351
           }
352
        }
353
        if(count_selected > 0){
354
          fq="&fq="+fq;
355
          allFqs += fq;
356
        }
357
      }
358
    }
359
    var doiQuery = "";
360
    var keywordQuery = "";
361
    if((this.searchUtils.keyword && this.searchUtils.keyword.length > 0)){
362
      if((this.type == 'publications' ||this.type == 'datasets')){
363
        var DOIs:string[] = DOI.getDOIsFromString(this.searchUtils.keyword);
364
        var doisParams = "";
365
        for(var i =0 ;i < DOIs.length; i++){
366
          doisParams+=(doisParams.length > 0?"&":"")+'doi="'+ DOIs[i]+'"';
367
        }
368
        if(doisParams.length > 0){
369
          doiQuery += "&"+doisParams;
370
        }
371
      }else{
372
         keywordQuery += " and ("+StringUtils.quote(StringUtils.URIEncode(this.searchUtils.keyword)) +")"
373

    
374
      }
375
    }
376
    return (doiQuery.length > 0 ? doiQuery:keywordQuery) + allFqs;
377

    
378
  }
379
  private isFiltered(){
380
    var filtered=false;
381
    for (let filter of this.filters){
382
       if(filter.countSelectedValues > 0){
383
           filtered = true;
384
           break;
385
       }
386
     }
387
    if(this.searchUtils.keyword.length > 0 ){
388
      filtered = true;
389
    }
390
    return filtered;
391
  }
392
  private clearKeywords(){
393
    if(this.searchUtils.keyword.length > 0 ){
394
      this.searchUtils.keyword ='';
395
    }
396
    this.goTo(1);
397
  }
398
  private clearFilters(){
399
    for (var i =0 ; i <  this.filters.length; i++){
400
         for (var j=0; j <  this.filters[i].countSelectedValues; j++){
401
          if(this.filters[i].values[j].selected){
402
            this.filters[i].values[j].selected = false;
403
           }
404
        this.filters[i].countSelectedValues = 0;
405
      }
406
    }
407
    this.clearKeywords();
408

    
409
  }
410
  private removeFilter(value:Value,filter:Filter){
411
    filter.countSelectedValues--;
412
    if(value.selected == true){
413
      value.selected = false;
414
     }
415
    this.goTo(1);
416

    
417
  }
418
  goTo(page:number = 1){
419
    this.searchUtils.page = page;
420
    console.info("searchUtils.page goto = "+this.searchUtils.page);
421
    this.queryParameters = new Map<string,string>();
422
    var urlParameters = this.createUrlParameters(this.filters,true);
423
    console.info("urlParams : "+urlParameters);
424
    this.updateBaseUrlWithParameters(this.filters);
425
    var queryParameters = this.createSearchQueryParameters(this.filters);
426
    console.info("queryParams : "+queryParameters);
427
    var indexQuery = this.createIndexQueryParameters(this.filters);
428

    
429
    this.location.go(location.pathname,urlParameters);
430

    
431
    this.queryChange.emit({
432
        value: queryParameters,
433
        index:indexQuery
434

    
435
    });
436

    
437
  }
438
  filterChanged($event){
439
       this.goTo(1);
440
  }
441
  keywordChanged($event) {
442
       this.searchUtils.keyword = $event.value;
443
       this.goTo(1);
444
  }
445

    
446
	downloadClicked($event) {
447
		if($event.value == true) {
448
		    var queryParameters = this.createSearchQueryParameters(this.filters);
449

    
450
		    this.downloadClick.emit({
451
		        value: queryParameters
452
		    });
453
		}
454
	}
455

    
456
  /*
457
  * Get A sub-array of this.refineFields array, which contains the ids of the selected filters
458
  */
459
  public getSelectedFilters():string[] {
460
    var selected:string[] = [];
461
    for(var i=0; i <  this.filters.length; i++){
462
      var filter:Filter = this.filters[i];
463
      if(filter.countSelectedValues > 0){
464
          selected.push(filter.filterId);
465
      }
466
    }
467
    return selected;
468
  }
469
  /*
470
  * Get A sub-array of this.refineFields array, which contains the ids of the selected parameters
471
  */
472
  private getSelectedParameters():string[] {
473
    var selected:string[] = [];
474
    var params:string[] = Object.keys(this.queryParameters);
475
    for(var i=0; i <  params.length; i++){
476
       if(this.refineFields.indexOf(params[i]) > -1){
477
          selected.push(params[i]);
478
      }
479
    }
480
    return selected;
481
  }
482
  /*
483
  * 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 )
484
  */
485
  public getFields():string[] {
486
    var selected_filters:string[] = this.getSelectedFilters();
487
    if(selected_filters.length == 0){
488
      selected_filters = this.getSelectedParameters();
489
    }
490
    var fields:string[] = [];
491
    for(var i =0 ; i < this.refineFields.length;i++){
492
      var dependentTo = this.searchFieldsHelper.DEPENDENT_FIELDS[this.refineFields[i]];
493

    
494
      //if filter is not marked as hidden OR it is hidden but it is dependent to a field that it IS selected
495
      if(this.searchFieldsHelper.HIDDEN_FIELDS.indexOf(this.refineFields[i]) == -1 || (selected_filters.indexOf(dependentTo) != -1) ){
496
          fields.push(this.refineFields[i]);
497
       }
498
    }
499
    return fields;
500
  }
501
  /*
502
  * Get a query  string of all fields, that want to get from search (e.g. &fields=funderid&fields=projectstartyear&...))
503
  */
504
  public getRefineFieldsQuery():string{
505

    
506
    var fields:string[] = this.getFields();
507
    var fieldsStr = ""
508
    for(var i =0 ; i < fields.length  ;i++){
509
        fieldsStr+="&fields="+fields[i];
510
    }
511
    return "&refine=true"+fieldsStr;
512
  }
513

    
514
  // for loading
515
  public openLoading(){
516
    if(this.loading){
517
      this.loading.open();
518
    }
519
  }
520
  public closeLoading(){
521
    if(this.loading){
522
      this.loading.close();
523
    }
524
  }
525
  getSelectedValues(filter):any{
526
    var selected = [];
527
    if(filter.countSelectedValues >0){
528
      for (var i=0; i < filter.values.length; i++){
529
        if(filter.values[i].selected){
530
          selected.push(filter.values[i]);
531
        }
532
      }
533
    }
534
    return selected;
535

    
536
  }
537
}
(16-16/22)