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

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

    
14
    <div class="uk-margin-top">
15
      <div class="page-header">
16
          <h1>{{pageTitle}}</h1>
17
      </div>
18
      <div>
19
        <div *ngIf="showRefine" class="uk-grid">
20
          <div  class="uk-width-1-1  uk-margin uk-panel uk-panel-box uk-panel-box-default">
21
              <search-form [(keyword)]="searchUtils.keyword" (keywordChange)="keywordChanged($event)"></search-form>
22
              <div *ngIf="isFiltered()" class = " uk-text-center ">
23
                <span *ngIf = "searchUtils.keyword.length > 0">Keywords:
24
                  <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 filter.values.slice(0,filter.countSelectedValues); let i = index;  let end = last; " >
29
                       {{value.name}} <a (click) = "removeFilter(value, filter) "> <span class=" e clickable" aria-hidden="true"><i class="uk-icon-remove"></i></span></a>
30
                       <span *ngIf="!end">,</span>
31
                    </span>
32
                  </span>
33
                </span>
34
                <a  (click)="clearFilters()"  class = "btn uk-text-right"> Clear Filters</a>
35
              </div>
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-download [totalResults]="searchUtils.totalResults" (downloadClick)="downloadClicked($event)"></search-download>
44
              <search-paging [type]="type" [(searchUtils)] = "searchUtils"  [(results)] = "results"  [(baseUrl)] = "baseURLWithParameters"></search-paging>
45
              <search-result [results]="results" [totalResults]="searchUtils.totalResults" [status]=searchUtils.status [page]="searchUtils.page"></search-result>
46
            </div>
47
          </div>
48
        </div>
49

    
50

    
51
        <div *ngIf="!showRefine" >
52

    
53
            <search-form [(keyword)]="searchUtils.keyword" (keywordChange)="keywordChanged($event)"></search-form>
54
            <search-paging [type]="type" [(searchUtils)] = "searchUtils"  [(results)] = "results"   [(baseUrl)] = "baseURLWithParameters"></search-paging>
55
             <search-result [results]="results" [totalResults]="searchUtils.totalResults" [status]=searchUtils.status [page]="searchUtils.page"></search-result>
56

    
57
        </div>
58
      </div>
59
    </div>
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
  public fieldIdsMap;//:  { [key:string]:{ name:string, operator:string, type:string, indexField:string, equalityOperator:string  }};
76
  private searchFieldsHelper:SearchFields = new SearchFields();
77
  private queryParameters: Map<string, string>  = new Map<string,string>();
78
  private baseURLWithParameters:string = '';
79
  private sub: any;
80

    
81
  constructor (private location: Location ) {
82

    
83
   }
84

    
85
  ngOnInit() {
86
        this.updateBaseUrlWithParameters(this.filters);
87
  }
88
  ngAfterViewChecked(){
89

    
90
  }
91

    
92
  public getQueryParametersFromUrl(params){
93
    //TODO when final search is done-allFqs/fq
94

    
95
    var parameters = "";
96
    var allFqs = "";
97

    
98
    for(var i=0; i< this.refineFields.length ; i++){
99
         var filterId =  this.refineFields[i];
100

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

    
121

    
122
   }
123
   var keyword = params['keyword'];
124
   console.info("Type:" + this.type + "filters: "+allFqs);
125
   var doiQuery = "";
126
   var keywordQuery = "";
127
   if((keyword && keyword.length > 0)){
128
     if((this.type == 'publications' ||this.type == 'datasets')){
129
       var DOIs:string[] = DOI.getDOIsFromString(keyword);
130
       var doisParams = "";
131

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

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

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

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

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

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

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

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

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

    
280
        for(var i =0 ;i < DOIs.length; i++){
281
          doisParams+=(doisParams.length > 0?" or ":"")+'((pidclassid exact doi) and (pid exact "'+ DOIs[i]+'"))';
282
        }
283
        if(doisParams.length > 0){
284
          doiQuery += "q=("+doisParams+")"
285
        }else{
286
          keywordQuery += "q=("+this.quote(this.searchUtils.keyword) +")"
287
        }
288
      }else{
289
         keywordQuery += "q=("+this.quote(this.searchUtils.keyword) +")"
290
      }
291
    }
292
    console.info("keyyyyword:::: doi:"+ doiQuery+ " keyword:"+keywordQuery )
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
    // console.info("SearchPAGE::page "+this.searchUtils.page);
387

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

    
392
    });
393

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

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

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

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

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

    
463
    var fields:string[] = this.getFields();
464
    var fieldsStr = ""
465
    for(var i =0 ; i < fields.length  ;i++){
466
        fieldsStr+="&fields="+fields[i];
467
    }
468
    return "&refine=true"+fieldsStr;
469
  }
470
  private quote(params: string):string {
471
      return encodeURIComponent('"'+params+'"');
472
  }
473

    
474
}
(7-7/10)