Skip to content

Commit

Permalink
search: use simple query for the search views
Browse files Browse the repository at this point in the history
* Uses AND operator by default.
* Makes the search engine more robust to the query syntax errors.
* Uses more simple syntax for boolean operator AND: +,OR: |, NOT: -.
* Fixes suggestion highlights by escaping regex special characters.

Co-Authored-by: Johnny Mariéthoz <Johnny.Mariethoz@rero.ch>
  • Loading branch information
jma committed May 15, 2020
1 parent 06844ac commit 8bad931
Show file tree
Hide file tree
Showing 16 changed files with 132 additions and 29 deletions.
55 changes: 52 additions & 3 deletions projects/admin/src/app/interceptor/no-cache-header.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,70 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { HttpHandler, HttpInterceptor, HttpRequest, HttpParameterCodec, HttpParams, HttpEvent } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';


class CustomEncoder implements HttpParameterCodec {

/**
* Encodes a key name for a URL parameter or query-string.
* @param key The key name.
* @returns The encoded key name.
*/
encodeKey(key: string): string {
return encodeURIComponent(key);
}

/**
* Encodes the value of a URL parameter or query-string.
* @param value The value.
* @returns The encoded value.
*/
encodeValue(value: string): string {
return encodeURIComponent(value);
}

/**
* Decodes an encoded URL parameter or query-string key.
* @param key The encoded key name.
* @returns The decoded key name.
*/
decodeKey(key: string): string {
return decodeURIComponent(key);
}

/**
* Decodes an encoded URL parameter or query-string value.
* @param value The encoded value.
* @returns The decoded value.
*/
decodeValue(value: string): string {
return decodeURIComponent(value);
}
}


@Injectable()
export class NoCacheHeaderInterceptor implements HttpInterceptor {

intercept(req: HttpRequest<any>, next: HttpHandler) {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

// encode URL parameters
// angular does not do it by default,
// see: https://github.com/angular/angular/issues/18261 for more details
const params = new HttpParams({ encoder: new CustomEncoder(), fromString: req.params.toString() });

const authReq = req.clone({

// Prevent caching in IE, in particular IE11.
// See: https://support.microsoft.com/en-us/help/234067/how-to-prevent-caching-in-internet-explorer
setHeaders: {
'Cache-Control': 'no-cache',
Pragma: 'no-cache'
}
});
return next.handle(authReq);
return next.handle(authReq.clone({ params }));
}
}
11 changes: 9 additions & 2 deletions projects/admin/src/app/menu/menu.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ import { MainTitlePipe } from '../pipe/main-title.pipe';
import { MenuService } from '../service/menu.service';
import { UserService } from '../service/user.service';


function escapeRegExp(data) {
return data.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}

@Component({
selector: 'admin-menu',
templateUrl: './menu.component.html',
Expand Down Expand Up @@ -185,7 +190,7 @@ export class MenuComponent implements OnInit {
const values = [];
persons.hits.hits.map(hit => {
let text = this.getPersonName(hit.metadata);
text = text.replace(new RegExp(query, 'gi'), `<b>${query}</b>`);
text = text.replace(new RegExp(escapeRegExp(query), 'gi'), `<b>${query}</b>`);
values.push({
text,
query: '',
Expand All @@ -208,7 +213,9 @@ export class MenuComponent implements OnInit {
truncate = true;
text = this._mainTitlePipe.transform(hit.metadata.title).substr(0, this.maxLengthSuggestion);
}
text = text.replace(new RegExp(query, 'gi'), `<b>${query}</b>`);
console.log(text);
text = text.replace(new RegExp(escapeRegExp(query), 'gi'), `<b>${query}</b>`);
console.log(text);
if (truncate) {
text = text + ' ...';
}
Expand Down
2 changes: 1 addition & 1 deletion projects/admin/src/app/record/editor/ref/ref.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class RefComponent extends FieldWrapper implements OnInit {
.getRecord('mef', v.split('/').pop(), 1)
.pipe(
map(data => {
for (const source of ['rero', 'bnf', 'gnd']) {
for (const source of ['rero', 'bnf', 'gnd', 'idref']) {
if (
data.metadata[source] &&
data.metadata[source].preferred_name_for_person
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class AcquisitionAccountsRoute extends BaseRoute implements RouteInterfac
return {
matcher: (url: any) => this.routeMatcher(url, this.name),
children: [
{ path: 'edit/:pid', component: EditorComponent, canActivate: [ CanUpdateGuard ] },
{ path: 'edit/:pid', component: EditorComponent, canActivate: [CanUpdateGuard] },
{ path: 'new', component: EditorComponent }
],
data: {
Expand Down
6 changes: 5 additions & 1 deletion projects/admin/src/app/routes/acquisition-orders-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class AcquisitionOrdersRoute extends BaseRoute implements RouteInterface
children: [
{ path: '', component: RecordSearchComponent },
{ path: 'detail/:pid', component: DetailComponent },
{ path: 'edit/:pid', component: EditorComponent, canActivate: [ CanUpdateGuard ] },
{ path: 'edit/:pid', component: EditorComponent, canActivate: [CanUpdateGuard] },
{ path: 'new', component: EditorComponent }
],
data: {
Expand All @@ -54,6 +54,10 @@ export class AcquisitionOrdersRoute extends BaseRoute implements RouteInterface
detailComponent: AcquisitionOrderDetailViewComponent,
canUpdate: (record: any) => this._routeToolService.canUpdate(record, this.recordType),
canDelete: (record: any) => this._routeToolService.canDelete(record, this.recordType),
// use simple query for UI search
preFilters: {
simple: 1
},
preCreateRecord: (data: any) => {
const user = this._routeToolService.userService.getCurrentUser();
data.order_date = formatDate(
Expand Down
15 changes: 10 additions & 5 deletions projects/admin/src/app/routes/budgets-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export class BudgetsRoute extends BaseRoute implements RouteInterface {
children: [
{ path: '', component: RecordSearchComponent },
{ path: 'detail/:pid', component: DetailComponent },
{ path: 'edit/:pid', component: EditorComponent, canActivate: [ CanUpdateGuard ] },
{ path: 'new', component: EditorComponent, canActivate: [ RoleGuard ], data: { roles: [ 'system_librarian' ]} }
{ path: 'edit/:pid', component: EditorComponent, canActivate: [CanUpdateGuard] },
{ path: 'new', component: EditorComponent, canActivate: [RoleGuard], data: { roles: ['system_librarian'] } }
],
data: {
linkPrefix: 'records',
Expand All @@ -53,13 +53,18 @@ export class BudgetsRoute extends BaseRoute implements RouteInterface {
canAdd: () => this._routeToolService.canSystemLibrarian(),
canUpdate: (record: any) => this._routeToolService.canUpdate(record, this.recordType),
canDelete: () => this._routeToolService.canNot(),
// use simple query for UI search
preFilters: {
simple: 1
},
preCreateRecord: (data: any) => {
const user = this._routeToolService.userService.getCurrentUser();
data.organisation = {
$ref: this._routeToolService.apiService.getRefEndpoint(
'organisations',
user.library.organisation.pid
)};
'organisations',
user.library.organisation.pid
)
};
return data;
}
}
Expand Down
10 changes: 7 additions & 3 deletions projects/admin/src/app/routes/circulation-policies-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export class CirculationPoliciesRoute extends BaseRoute implements RouteInterfac
children: [
{ path: '', component: RecordSearchComponent },
{ path: 'detail/:pid', component: DetailComponent },
{ path: 'edit/:pid', component: CirculationPolicyComponent, canActivate: [ CanUpdateGuard ] },
{ path: 'new', component: CirculationPolicyComponent, canActivate: [ RoleGuard ], data: { roles: [ 'system_librarian' ]} }
{ path: 'edit/:pid', component: CirculationPolicyComponent, canActivate: [CanUpdateGuard] },
{ path: 'new', component: CirculationPolicyComponent, canActivate: [RoleGuard], data: { roles: ['system_librarian'] } }
],
data: {
linkPrefix: 'records',
Expand All @@ -53,7 +53,11 @@ export class CirculationPoliciesRoute extends BaseRoute implements RouteInterfac
detailComponent: CircPolicyDetailViewComponent,
canAdd: () => this._routeToolService.canSystemLibrarian(),
canUpdate: (record: any) => this._routeToolService.canUpdate(record, this.recordType),
canDelete: (record: any) => this._routeToolService.canDelete(record, this.recordType)
canDelete: (record: any) => this._routeToolService.canDelete(record, this.recordType),
// use simple query for UI search
preFilters: {
simple: 1
}
}
]
}
Expand Down
4 changes: 4 additions & 0 deletions projects/admin/src/app/routes/documents-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ export class DocumentsRoute extends BaseRoute implements RouteInterface {
detailComponent: DocumentDetailViewComponent,
canUpdate: (record: any) => this._routeToolService.canUpdate(record, this.recordType),
canDelete: (record: any) => this._routeToolService.canDelete(record, this.recordType),
// use simple query for UI search
preFilters: {
simple: 1
},
preprocessRecordEditor: (record: any) => {
return this.removeKey(record, '_text');
},
Expand Down
8 changes: 6 additions & 2 deletions projects/admin/src/app/routes/item-types-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export class ItemTypesRoute extends BaseRoute implements RouteInterface {
children: [
{ path: '', component: RecordSearchComponent },
{ path: 'detail/:pid', component: DetailComponent },
{ path: 'edit/:pid', component: EditorComponent, canActivate: [ CanUpdateGuard ] },
{ path: 'new', component: EditorComponent, canActivate: [ RoleGuard ], data: { roles: [ 'system_librarian' ]} }
{ path: 'edit/:pid', component: EditorComponent, canActivate: [CanUpdateGuard] },
{ path: 'new', component: EditorComponent, canActivate: [RoleGuard], data: { roles: ['system_librarian'] } }
],
data: {
linkPrefix: 'records',
Expand All @@ -53,6 +53,10 @@ export class ItemTypesRoute extends BaseRoute implements RouteInterface {
canAdd: () => this._routeToolService.canSystemLibrarian(),
canUpdate: (record: any) => this._routeToolService.canUpdate(record, this.recordType),
canDelete: (record: any) => this._routeToolService.canDelete(record, this.recordType),
// use simple query for UI search
preFilters: {
simple: 1
},
preCreateRecord: (data: any) => {
const user = this._routeToolService.userService.getCurrentUser();
data.organisation = {
Expand Down
8 changes: 6 additions & 2 deletions projects/admin/src/app/routes/libraries-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export class LibrariesRoute extends BaseRoute implements RouteInterface {
children: [
{ path: '', component: RecordSearchComponent },
{ path: 'detail/:pid', component: DetailComponent },
{ path: 'edit/:pid', component: LibraryComponent, canActivate: [ CanUpdateGuard ] },
{ path: 'new', component: LibraryComponent, canActivate: [ RoleGuard ], data: { roles: [ 'system_librarian' ]} }
{ path: 'edit/:pid', component: LibraryComponent, canActivate: [CanUpdateGuard] },
{ path: 'new', component: LibraryComponent, canActivate: [RoleGuard], data: { roles: ['system_librarian'] } }
],
data: {
linkPrefix: 'records',
Expand All @@ -54,6 +54,10 @@ export class LibrariesRoute extends BaseRoute implements RouteInterface {
canAdd: () => this._routeToolService.canSystemLibrarian(),
canUpdate: (record: any) => this._routeToolService.canUpdate(record, this.recordType),
canDelete: (record: any) => this._routeToolService.canDelete(record, this.recordType),
// use simple query for UI search
preFilters: {
simple: 1
},
preCreateRecord: (data: any) => {
const user = this._routeToolService.userService.getCurrentUser();
data.organisation = {
Expand Down
8 changes: 6 additions & 2 deletions projects/admin/src/app/routes/patron-types-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export class PatronTypesRoute extends BaseRoute implements RouteInterface {
children: [
{ path: '', component: RecordSearchComponent },
{ path: 'detail/:pid', component: DetailComponent },
{ path: 'edit/:pid', component: EditorComponent, canActivate: [ CanUpdateGuard ] },
{ path: 'new', component: EditorComponent, canActivate: [ RoleGuard ], data: { roles: [ 'system_librarian' ]} }
{ path: 'edit/:pid', component: EditorComponent, canActivate: [CanUpdateGuard] },
{ path: 'new', component: EditorComponent, canActivate: [RoleGuard], data: { roles: ['system_librarian'] } }
],
data: {
linkPrefix: 'records',
Expand All @@ -54,6 +54,10 @@ export class PatronTypesRoute extends BaseRoute implements RouteInterface {
canAdd: () => this._routeToolService.canSystemLibrarian(),
canUpdate: (record: any) => this._routeToolService.canUpdate(record, this.recordType),
canDelete: (record: any) => this._routeToolService.canDelete(record, this.recordType),
// use simple query for UI search
preFilters: {
simple: 1
},
preCreateRecord: (data: any) => {
const user = this._routeToolService.userService.getCurrentUser();
data.organisation = {
Expand Down
8 changes: 6 additions & 2 deletions projects/admin/src/app/routes/patrons-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class PatronsRoute extends BaseRoute implements RouteInterface {
children: [
{ path: '', component: RecordSearchComponent },
{ path: 'detail/:pid', component: DetailComponent },
{ path: 'edit/:pid', component: EditorComponent, canActivate: [ CanUpdateGuard ] },
{ path: 'edit/:pid', component: EditorComponent, canActivate: [CanUpdateGuard] },
{ path: 'new', component: EditorComponent }
],
data: {
Expand All @@ -51,14 +51,18 @@ export class PatronsRoute extends BaseRoute implements RouteInterface {
detailComponent: PatronDetailViewComponent,
canUpdate: (record: any) => this._routeToolService.canUpdate(record, this.recordType),
canDelete: (record: any) => this._routeToolService.canDelete(record, this.recordType),
aggregationsExpand: ['roles'],
// Clean-up 'blocked_note' field content if blocked is false.
postprocessRecordEditor: (record: any) => {
if (record.blocked === false) {
delete record.blocked_note;
}
return record;
},
// use simple query for UI search
preFilters: {
simple: 1
},
aggregationsExpand: ['roles']
}
]
}
Expand Down
6 changes: 5 additions & 1 deletion projects/admin/src/app/routes/persons-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ export class PersonsRoute extends BaseRoute implements RouteInterface {
label: 'Persons',
component: PersonsBriefViewComponent,
detailComponent: PersonDetailViewComponent,
aggregationsExpand: ['sources']
aggregationsExpand: ['sources'],
// use simple query for UI search
preFilters: {
simple: 1
}
}
]
}
Expand Down
4 changes: 4 additions & 0 deletions projects/admin/src/app/routes/vendors-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export class VendorsRoute extends BaseRoute implements RouteInterface {
detailComponent: VendorDetailViewComponent,
canUpdate: (record: any) => this._routeToolService.canUpdate(record, this.recordType),
canDelete: (record: any) => this._routeToolService.canDelete(record, this.recordType),
// use simple query for UI search
preFilters: {
simple: 1
},
preCreateRecord: (data: any) => {
const user = this._routeToolService.userService.getCurrentUser();
data.organisation = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { TranslateService } from '@ngx-translate/core';
import { MainTitlePipe } from '../../../../admin/src/app/pipe/main-title.pipe';


function escapeRegExp(data) {
return data.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}

@Component({
selector: 'public-search-search-bar',
templateUrl: './search-bar.component.html',
Expand Down Expand Up @@ -59,7 +63,7 @@ export class SearchBarComponent implements OnInit {
const values = [];
persons.hits.hits.map(hit => {
let text = SearchBarComponent.getPersonName(hit.metadata);
text = text.replace(new RegExp(query, 'gi'), `<b>${query}</b>`);
text = text.replace(new RegExp(escapeRegExp(query), 'gi'), `<b>${query}</b>`);
values.push({
text,
query: '',
Expand All @@ -81,7 +85,7 @@ export class SearchBarComponent implements OnInit {
truncate = true;
text = this._mainTitlePipe.transform(hit.metadata.title).substr(0, this.maxLengthSuggestion);
}
text = text.replace(new RegExp(query, 'gi'), `<b>${query}</b>`);
text = text.replace(new RegExp(escapeRegExp(query), 'gi'), `<b>${query}</b>`);
if (truncate) {
text = text + ' ...';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ export class RoutingInitService {
aggregationsExpand: ['document_type'],
aggregationsBucketSize: 10,
preFilters: {
view: `${viewcode}`
view: `${viewcode}`,
simple: 1
},
listHeaders: {
Accept: 'application/rero+json, application/json'
Expand All @@ -89,7 +90,8 @@ export class RoutingInitService {
Accept: 'application/rero+json, application/json'
},
preFilters: {
view: `${viewcode}`
view: `${viewcode}`,
simple: 1
}
}
]
Expand Down

0 comments on commit 8bad931

Please sign in to comment.