[ADF-2189] new search facets (#3230)

* slider facet

* number range facet

* unit tests

* remove fdescribe

* remove old demo-only content

* remove old tests

* Support "include" and "field" values with app config

* exclude deprecated demo content from export checks
This commit is contained in:
Denys Vuika
2018-04-25 17:45:14 +01:00
committed by Eugenio Romano
parent b7fc44d576
commit 3a51c27f66
24 changed files with 493 additions and 323 deletions

View File

@@ -53,6 +53,8 @@
}
],
"search": {
"include": ["path", "allowableOperations"],
"fields": [],
"filterQueries": [
{ "query": "TYPE:'cm:folder' OR TYPE:'cm:content'" },
{ "query": "NOT cm:creator:System" }
@@ -78,85 +80,62 @@
"query": {
"categories": [
{
"id": "broken",
"name": "Broken Facet",
"enabled": false,
"expanded": false,
"component": {
"selector": "text",
"settings": {
"field": "fieldname"
"id": "queryName",
"name": "Name",
"enabled": true,
"expanded": true,
"component": {
"selector": "text",
"settings": {
"pattern": "cm:name:'(.*?)'",
"field": "cm:name",
"placeholder": "Enter the name"
}
}
},
{
"id": "contentSize",
"name": "Content Size",
"enabled": true,
"component": {
"selector": "slider",
"settings": {
"field": "cm:content.size",
"min": 0,
"max": 18,
"step": 1,
"thumbLabel": true
}
}
},
{
"id": "contentSizeRange",
"name": "Content Size (range)",
"enabled": true,
"component": {
"selector": "number-range",
"settings": {
"field": "cm:content.size"
}
}
},
{
"id": "queryType",
"name": "Type",
"enabled": true,
"component": {
"selector": "radio",
"settings": {
"field": null,
"options": [
{ "name": "None", "value": "", "default": true },
{ "name": "All", "value": "TYPE:'cm:folder' OR TYPE:'cm:content'" },
{ "name": "Folder", "value": "TYPE:'cm:folder'" },
{ "name": "Document", "value": "TYPE:'cm:content'" }
]
}
}
}
},
{
"id": "queryName",
"name": "Name",
"enabled": true,
"expanded": true,
"component": {
"selector": "text",
"settings": {
"pattern": "cm:name:'(.*?)'",
"field": "cm:name",
"placeholder": "Enter the name"
}
}
},
{
"id": "queryFields",
"name": "Fields",
"enabled": true,
"expanded": false,
"component": {
"selector": "fields",
"settings": {
"field": null,
"options": [
{ "name": "Name", "value": "name", "fields": ["name"], "default": true },
{ "name": "File Size", "value": "content.sizeInBytes", "fields": ["content"], "default": true },
{ "name": "Modified On", "value": "modifiedAt", "fields": ["modifiedAt"], "default": true },
{ "name": "Modified By", "value": "modifiedByUser.displayName", "fields": ["modifiedByUser"], "default": true }
]
}
}
},
{
"id": "queryType",
"name": "Type",
"enabled": true,
"expanded": false,
"component": {
"selector": "radio",
"settings": {
"field": null,
"options": [
{ "name": "None", "value": "", "default": true },
{ "name": "All", "value": "TYPE:'cm:folder' OR TYPE:'cm:content'" },
{ "name": "Folder", "value": "TYPE:'cm:folder'" },
{ "name": "Document", "value": "TYPE:'cm:content'" }
]
}
}
},
{
"id": "queryLocations",
"name": "Locations",
"enabled": true,
"expanded": false,
"component": {
"selector": "scope-locations",
"settings": {
"field": null,
"options": [
{ "name": "Default", "value": "nodes", "default": true },
{ "name": "Nodes", "value": "nodes" },
{ "name": "Deleted Nodes", "value": "deleted-nodes" },
{ "name": "Versions", "value": "versions" }
]
}
}
}
]
}
},

View File

@@ -5109,7 +5109,8 @@
"character": 8,
"fileName": "lib/content-services/search/components/search-widget-container/search-widgets.module.ts"
},
"name": "SearchFieldsComponent"
"name": "SearchFieldsComponent",
"skipError": true
},
{
"position": {
@@ -5173,7 +5174,8 @@
"character": 8,
"fileName": "lib/content-services/search/components/search-widget-container/search-widgets.module.ts"
},
"name": "SearchScopeLocationsComponent"
"name": "SearchScopeLocationsComponent",
"skipError": true
},
{
"position": {
@@ -6455,4 +6457,4 @@
},
"name": "WidgetComponent"
}
]
]

View File

@@ -165,6 +165,16 @@
"MODIFIED_AT": "Modified at"
}
},
"FILTER": {
"ACTIONS": {
"CLEAR": "Clear",
"APPLY": "Apply"
},
"RANGE": {
"FROM": "From",
"TO": "To"
}
},
"ICONS": {
"ft_ic_raster_image": "Image file",
"ft_ic_pdf": "PDF document",

View File

@@ -34,7 +34,8 @@ import {
MatCheckboxModule,
MatDatepickerModule,
MatSlideToggleModule,
MatRadioModule
MatRadioModule,
MatSliderModule
} from '@angular/material';
export function modules() {
@@ -56,7 +57,8 @@ export function modules() {
MatCheckboxModule,
MatDatepickerModule,
MatSlideToggleModule,
MatRadioModule
MatRadioModule,
MatSliderModule
];
}

View File

@@ -1,6 +0,0 @@
<mat-checkbox
*ngFor="let option of settings.options"
[checked]="option.checked"
(change)="changeHandler($event, option)">
{{ option.name }}
</mat-checkbox>

View File

@@ -1,8 +0,0 @@
.adf-search-fields {
display: flex;
flex-direction: column;
.mat-checkbox {
margin: 5px;
}
}

View File

@@ -1,70 +0,0 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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, ViewEncapsulation, OnInit, Input } from '@angular/core';
import { MatCheckboxChange } from '@angular/material';
import { SearchWidget } from '../../search-widget.interface';
import { SearchWidgetSettings } from '../../search-widget-settings.interface';
import { SearchQueryBuilderService } from '../../search-query-builder.service';
@Component({
selector: 'adf-search-fields',
templateUrl: './search-fields.component.html',
styleUrls: ['./search-fields.component.scss'],
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-search-fields' }
})
export class SearchFieldsComponent implements SearchWidget, OnInit {
@Input()
value: string;
id: string;
settings: SearchWidgetSettings;
context: SearchQueryBuilderService;
ngOnInit() {
const defaultOptions = (this.settings.options || [])
.filter(opt => opt.default)
.map(opt => {
opt.checked = true;
return opt;
});
if (defaultOptions.length > 0) {
this.flush(defaultOptions);
}
}
changeHandler(event: MatCheckboxChange, option: any) {
option.checked = event.checked;
this.flush(this.settings.options);
}
flush(opts: any[] = []) {
const checkedValues = opts
.filter(v => v.checked)
.map(v => v.fields)
.reduce((prev, curr) => {
return prev.concat(curr);
}, []);
this.context.fields[this.id] = checkedValues;
this.context.update();
}
}

View File

@@ -18,8 +18,8 @@
import { Injectable, Type } from '@angular/core';
import { SearchTextComponent } from '../search-text/search-text.component';
import { SearchRadioComponent } from '../search-radio/search-radio.component';
import { SearchFieldsComponent } from '../search-fields/search-fields.component';
import { SearchScopeLocationsComponent } from '../search-scope-locations/search-scope-locations.component';
import { SearchSliderComponent } from '../search-slider/search-slider.component';
import { SearchNumberRangeComponent } from '../search-number-range/search-number-range.component';
@Injectable()
export class SearchFilterService {
@@ -30,8 +30,8 @@ export class SearchFilterService {
widgets: { [id: string]: Type<{}> } = {
'text': SearchTextComponent,
'radio': SearchRadioComponent,
'fields': SearchFieldsComponent,
'scope-locations': SearchScopeLocationsComponent
'slider': SearchSliderComponent,
'number-range': SearchNumberRangeComponent
};
}

View File

@@ -0,0 +1,27 @@
<form [formGroup]="form" novalidate (ngSubmit)="apply(form.value, form.valid)">
<mat-form-field>
<input
matInput [formControl]="from" [errorStateMatcher]="matcher"
placeholder="{{ 'SEARCH.FILTER.RANGE.FROM' | translate }}">
<mat-error *ngIf="from.hasError('pattern')">Invalid format</mat-error>
<mat-error *ngIf="from.hasError('required')">Required value</mat-error>
</mat-form-field>
<mat-form-field >
<input
matInput [formControl]="to" [errorStateMatcher]="matcher"
placeholder="{{ 'SEARCH.FILTER.RANGE.TO' | translate }}">
<mat-error *ngIf="to.invalid">Invalid format</mat-error>
</mat-form-field>
<div>
<button mat-button color="primary" (click)="reset()">
{{ 'SEARCH.FILTER.ACTIONS.CLEAR' | translate }}
</button>
<button mat-button color="primary" type="submit" [disabled]="!form.valid">
{{ 'SEARCH.FILTER.ACTIONS.APPLY' | translate }}
</button>
</div>
</form>

View File

@@ -0,0 +1,8 @@
.adf-search-number-range > form {
display: inline-flex;
flex-direction: column;
.mat-button {
text-transform: uppercase;
}
}

View File

@@ -0,0 +1,88 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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 { SearchNumberRangeComponent } from './search-number-range.component';
describe('SearchNumberRangeComponent', () => {
let component: SearchNumberRangeComponent;
beforeEach(() => {
component = new SearchNumberRangeComponent();
});
it('should setup form elements on init', () => {
component.ngOnInit();
expect(component.form).toBeDefined();
expect(component.to).toBeDefined();
expect(component.form).toBeDefined();
});
it('should reset form', () => {
component.ngOnInit();
component.form.reset({ from: '10', to: '20' });
component.reset();
expect(component.from.value).toEqual('');
expect(component.to.value).toEqual('');
expect(component.form.value).toEqual({ from: '', to: '' });
});
it('should update query builder on reset', () => {
const context: any = {
queryFragments: {
contentSize: 'query'
},
update() {}
};
component.id = 'contentSize';
component.context = context;
spyOn(context, 'update').and.stub();
component.ngOnInit();
component.reset();
expect(context.queryFragments.contentSize).toEqual('');
expect(context.update).toHaveBeenCalled();
});
it('should update query builder on value changes', () => {
const context: any = {
queryFragments: {},
update() {}
};
component.id = 'contentSize';
component.context = context;
component.settings = { field: 'cm:content.size' };
spyOn(context, 'update').and.stub();
component.ngOnInit();
component.apply({
from: '10',
to: '20'
}, true);
const expectedQuery = 'cm:content.size:[10 TO 20]';
expect(context.queryFragments[component.id]).toEqual(expectedQuery);
expect(context.update).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,77 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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 { OnInit, Component, ViewEncapsulation } from '@angular/core';
import { FormControl, Validators, FormGroup } from '@angular/forms';
import { SearchWidget } from '../../search-widget.interface';
import { SearchWidgetSettings } from '../../search-widget-settings.interface';
import { SearchQueryBuilderService } from '../../search-query-builder.service';
import { LiveErrorStateMatcher } from '../../forms/live-error-state-matcher';
@Component({
selector: 'adf-search-number-range',
templateUrl: './search-number-range.component.html',
styleUrls: ['./search-number-range.component.scss'],
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-search-number-range' }
})
export class SearchNumberRangeComponent implements SearchWidget, OnInit {
from: FormControl;
to: FormControl;
form: FormGroup;
matcher = new LiveErrorStateMatcher();
id: string;
settings?: SearchWidgetSettings;
context?: SearchQueryBuilderService;
ngOnInit(): void {
const validators = Validators.compose([
Validators.required,
Validators.pattern(/^-?(0|[1-9]\d*)?$/)
]);
this.from = new FormControl('', validators);
this.to = new FormControl('', validators);
this.form = new FormGroup({
from: this.from,
to: this.to
});
}
apply(model: { from: string, to: string }, isValid: boolean) {
if (isValid && this.id && this.context && this.settings && this.settings.field) {
this.context.queryFragments[this.id] = `${this.settings.field}:[${model.from} TO ${model.to}]`;
this.context.update();
}
}
reset() {
this.form.reset({
from: '',
to: ''
});
if (this.id && this.context) {
this.context.queryFragments[this.id] = '';
this.context.update();
}
}
}

View File

@@ -1,11 +0,0 @@
<mat-form-field>
<mat-select
[(value)]="value"
(selectionChange)="changeHandler($event)">
<mat-option
*ngFor="let option of settings.options"
[value]="option.value">
{{option.name}}
</mat-option>
</mat-select>
</mat-form-field>

View File

@@ -1,57 +0,0 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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, ViewEncapsulation, OnInit, Input } from '@angular/core';
import { MatSelectChange } from '@angular/material';
import { SearchWidget } from '../../search-widget.interface';
import { SearchWidgetSettings } from '../../search-widget-settings.interface';
import { SearchQueryBuilderService } from '../../search-query-builder.service';
@Component({
selector: 'adf-search-scope-locations',
templateUrl: './search-scope-locations.component.html',
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-search-scope-locations' }
})
export class SearchScopeLocationsComponent implements SearchWidget, OnInit {
@Input()
value: string;
id: string;
settings: SearchWidgetSettings;
context: SearchQueryBuilderService;
ngOnInit() {
const defaultSelection = (this.settings.options || []).find(opt => opt.default);
if (defaultSelection) {
this.flush(defaultSelection.value);
}
}
changeHandler(event: MatSelectChange) {
this.flush(event.value);
}
flush(value: string) {
this.value = value;
this.context.scope.locations = value;
this.context.update();
}
}

View File

@@ -0,0 +1,8 @@
<mat-slider
[value]="value"
[min]="min"
[max]="max"
[step]="step"
[thumbLabel]="thumbLabel"
(change)="onChangedHandler($event)">
</mat-slider>

View File

@@ -0,0 +1,5 @@
.adf-search-slider {
.mat-slider {
width: 100%;
}
}

View File

@@ -0,0 +1,73 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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 { MatSliderChange } from '@angular/material';
import { SearchSliderComponent } from './search-slider.component';
describe('SearchSliderComponent', () => {
let component: SearchSliderComponent;
beforeEach(() => {
component = new SearchSliderComponent();
});
it('should setup slider from settings', () => {
const settings: any = {
min: 10,
max: 100,
step: 2,
thumbLabel: true
};
component.settings = settings;
component.ngOnInit();
expect(component.min).toEqual(settings.min);
expect(component.max).toEqual(settings.max);
expect(component.step).toEqual(settings.step);
expect(component.thumbLabel).toEqual(settings.thumbLabel);
});
it('should update value on slider change', () => {
component.onChangedHandler(<MatSliderChange> { value: 10 });
expect(component.value).toEqual(10);
component.onChangedHandler(<MatSliderChange> { value: 20 });
expect(component.value).toEqual(20);
});
it('should update its query part on slider change', () => {
const context: any = {
queryFragments: {},
update() {}
};
spyOn(context, 'update').and.stub();
component.context = context;
component.id = 'contentSize';
component.settings = { field: 'cm:content.size' };
component.onChangedHandler(<MatSliderChange> { value: 10 });
const expectedQuery = 'cm:content.size:[0 TO 10]';
expect(context.queryFragments[component.id]).toEqual(expectedQuery);
expect(context.update).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,69 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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, ViewEncapsulation, OnInit } from '@angular/core';
import { SearchWidget } from '../../search-widget.interface';
import { SearchWidgetSettings } from '../../search-widget-settings.interface';
import { SearchQueryBuilderService } from '../../search-query-builder.service';
import { MatSliderChange } from '@angular/material';
@Component({
selector: 'adf-search-slider',
templateUrl: './search-slider.component.html',
styleUrls: ['./search-slider.component.scss'],
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-search-slider' }
})
export class SearchSliderComponent implements SearchWidget, OnInit {
id: string;
settings: SearchWidgetSettings;
context: SearchQueryBuilderService;
step: number;
min: number;
max: number;
thumbLabel = false;
value: number;
ngOnInit() {
if (this.settings) {
if (this.settings.hasOwnProperty('min')) {
this.min = this.settings['min'];
}
if (this.settings.hasOwnProperty('max')) {
this.max = this.settings['max'];
}
if (this.settings.hasOwnProperty('step')) {
this.step = this.settings['step'];
}
this.thumbLabel = this.settings['thumbLabel'] ? true : false;
}
}
onChangedHandler(event: MatSliderChange) {
this.value = event.value;
if (this.id && this.context && this.settings && this.settings.field) {
this.context.queryFragments[this.id] = `${this.settings.field}:[0 TO ${this.value}]`;
this.context.update();
}
}
}

View File

@@ -0,0 +1,28 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* 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 { ErrorStateMatcher } from '@angular/material';
import { FormControl, FormGroupDirective, NgForm } from '@angular/forms';
export class LiveErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const isSubmitted = form && form.submitted;
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
}
}

View File

@@ -21,13 +21,11 @@ import { FacetField } from './facet-field.interface';
import { SearchCategory } from './search-category.interface';
export interface SearchConfiguration {
include?: Array<string>;
fields?: Array<string>;
query?: {
categories: Array<SearchCategory>
};
limits?: {
permissionEvaluationTime?: number;
permissionEvaluationCount?: number;
};
filterQueries?: Array<FilterQuery>;
facetQueries?: Array<FacetQuery>;
facetFields?: {

View File

@@ -69,13 +69,6 @@ describe('SearchQueryBuilder', () => {
expect(builder.filterQueries[1].query).toBe('query2');
});
it('should setup default location scope', () => {
const builder = new SearchQueryBuilderService(buildConfig({}), null);
expect(builder.scope).toBeDefined();
expect(builder.scope.locations).toBeNull();
});
it('should add new filter query', () => {
const builder = new SearchQueryBuilderService(buildConfig({}), null);
@@ -237,6 +230,7 @@ describe('SearchQueryBuilder', () => {
it('should build query with custom fields', () => {
const config: SearchConfiguration = {
fields: ['field1', 'field2'],
query: {
categories: [
<any> { id: 'cat1', enabled: true },
@@ -247,15 +241,14 @@ describe('SearchQueryBuilder', () => {
const builder = new SearchQueryBuilderService(buildConfig(config), null);
builder.queryFragments['cat1'] = 'cm:name:test';
builder.fields['cat1'] = ['field1', 'field3'];
builder.fields['cat2'] = ['field2', 'field3'];
const compiled = builder.buildQuery();
expect(compiled.fields).toEqual(['field1', 'field3', 'field2']);
expect(compiled.fields).toEqual(['field1', 'field2']);
});
it('should build query with empty custom fields', () => {
const config: SearchConfiguration = {
fields: [],
query: {
categories: [
<any> { id: 'cat1', enabled: true },
@@ -266,8 +259,6 @@ describe('SearchQueryBuilder', () => {
const builder = new SearchQueryBuilderService(buildConfig(config), null);
builder.queryFragments['cat1'] = 'cm:name:test';
builder.fields['cat1'] = [];
builder.fields['cat2'] = null;
const compiled = builder.buildQuery();
expect(compiled.fields).toEqual([]);
@@ -330,41 +321,6 @@ describe('SearchQueryBuilder', () => {
expect(compiled.facetFields).toEqual(config.facetFields);
});
it('should build query with custom limits', () => {
const config: SearchConfiguration = {
query: {
categories: [
<any> { id: 'cat1', enabled: true }
]
},
limits: {
permissionEvaluationCount: 100,
permissionEvaluationTime: 100
}
};
const builder = new SearchQueryBuilderService(buildConfig(config), null);
builder.queryFragments['cat1'] = 'cm:name:test';
const compiled = builder.buildQuery();
expect(compiled.limits).toEqual(config.limits);
});
it('should build query with custom scope', () => {
const config: SearchConfiguration = {
query: {
categories: [
<any> { id: 'cat1', enabled: true }
]
}
};
const builder = new SearchQueryBuilderService(buildConfig(config), null);
builder.queryFragments['cat1'] = 'cm:name:test';
builder.scope.locations = 'custom';
const compiled = builder.buildQuery();
expect(compiled.scope.locations).toEqual('custom');
});
it('should use pagination settings', () => {
const config: SearchConfiguration = {
query: {

View File

@@ -33,8 +33,6 @@ export class SearchQueryBuilderService {
categories: Array<SearchCategory> = [];
queryFragments: { [id: string]: string } = {};
fields: { [id: string]: string[] } = {};
scope: { locations?: string };
filterQueries: FilterQuery[] = [];
ranges: { [id: string]: SearchRange } = {};
paging: { maxItems?: number; skipCount?: number } = null;
@@ -52,9 +50,6 @@ export class SearchQueryBuilderService {
}
this.filterQueries = this.config.filterQueries || [];
this.scope = {
locations: null
};
}
addFilterQuery(query: string): void {
@@ -93,7 +88,6 @@ export class SearchQueryBuilderService {
buildQuery(): QueryBody {
let query = '';
const fields: string[] = [];
this.categories.forEach(facet => {
const customQuery = this.queryFragments[facet.id];
@@ -103,17 +97,13 @@ export class SearchQueryBuilderService {
}
query += `(${customQuery})`;
}
const customFields = this.fields[facet.id];
if (customFields && customFields.length > 0) {
for (const field of customFields) {
if (!fields.includes(field)) {
fields.push(field);
}
}
}
});
const include = this.config.include || [];
if (include.length === 0) {
include.push('path', 'allowableOperations');
}
if (query) {
const result: QueryBody = {
@@ -121,14 +111,12 @@ export class SearchQueryBuilderService {
query: query,
language: 'afts'
},
include: ['path', 'allowableOperations'],
fields: fields,
include: include,
paging: this.paging,
fields: this.config.fields,
filterQueries: this.filterQueries,
facetQueries: this.config.facetQueries,
facetFields: this.config.facetFields,
limits: this.config.limits,
scope: this.scope
facetFields: this.config.facetFields
};
return result;

View File

@@ -33,8 +33,8 @@ import { SearchFilterComponent } from './components/search-filter/search-filter.
import { SearchChipListComponent } from './components/search-chip-list/search-chip-list.component';
import { SearchTextComponent } from './components/search-text/search-text.component';
import { SearchRadioComponent } from './components/search-radio/search-radio.component';
import { SearchFieldsComponent } from './components/search-fields/search-fields.component';
import { SearchScopeLocationsComponent } from './components/search-scope-locations/search-scope-locations.component';
import { SearchSliderComponent } from './components/search-slider/search-slider.component';
import { SearchNumberRangeComponent } from './components/search-number-range/search-number-range.component';
export const ALFRESCO_SEARCH_DIRECTIVES: any[] = [
SearchComponent,
@@ -59,23 +59,23 @@ export const ALFRESCO_SEARCH_DIRECTIVES: any[] = [
SearchWidgetContainerComponent,
SearchTextComponent,
SearchRadioComponent,
SearchFieldsComponent,
SearchScopeLocationsComponent
SearchSliderComponent,
SearchNumberRangeComponent
],
exports: [
...ALFRESCO_SEARCH_DIRECTIVES,
SearchWidgetContainerComponent,
SearchTextComponent,
SearchRadioComponent,
SearchFieldsComponent,
SearchScopeLocationsComponent
SearchSliderComponent,
SearchNumberRangeComponent
],
entryComponents: [
SearchWidgetContainerComponent,
SearchTextComponent,
SearchRadioComponent,
SearchFieldsComponent,
SearchScopeLocationsComponent
SearchSliderComponent,
SearchNumberRangeComponent
]
})
export class SearchModule {}

View File

@@ -481,12 +481,16 @@
"description": "Search configuration parameters",
"type": "object",
"properties": {
"limits": {
"description": "The limits element limits the time and resources used for query execution. Limits applied to the query go to the database.",
"type": "object",
"properties": {
"permissionEvaluationTime": { "type": "integer" },
"permissionEvaluationCount": { "type": "integer" }
"include": {
"type": "array",
"items": {
"type": "string"
}
},
"fields": {
"type": "array",
"items": {
"type": "string"
}
},
"filterQueries": {