From: Mike Rylander Date: Tue, 10 Mar 2020 13:34:51 +0000 (-0400) Subject: WIP: complex, cross-table searches X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=e64d6daae4eb8f3a5d2acca3a5b172de74ba240a;p=working%2FEvergreen.git WIP: complex, cross-table searches Signed-off-by: Mike Rylander Signed-off-by: Galen Charlton --- diff --git a/Open-ILS/src/eg2/src/app/staff/acq/provider/acq-provider-search-form.component.ts b/Open-ILS/src/eg2/src/app/staff/acq/provider/acq-provider-search-form.component.ts index 79f9822481..afd33bfc57 100644 --- a/Open-ILS/src/eg2/src/app/staff/acq/provider/acq-provider-search-form.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/acq/provider/acq-provider-search-form.component.ts @@ -68,37 +68,38 @@ export class AcqProviderSearchFormComponent implements OnInit, AfterViewInit { const searchTerms: AcqProviderSearchTerm[] = []; if (this.providerName) { - searchTerms.push({ field: 'name', op: 'ilike', value: this.providerName }); + searchTerms.push({ fields: ['name'], op: 'ilike', value: this.providerName }); } if (this.providerCode) { - searchTerms.push({ field: 'code', op: 'ilike', value: this.providerCode }); + searchTerms.push({ fields: ['code'], op: 'ilike', value: this.providerCode }); } if (this.providerOwners) { - searchTerms.push({ field: 'owner', op: 'in', value: this.providerOwners.orgIds }); + searchTerms.push({ fields: ['owner'], op: 'in', value: this.providerOwners.orgIds }); } if (this.contactName) { - // TODO + searchTerms.push({ classes: ['acqpc'], fields: ['name'], op: 'ilike', value: this.contactName }); } if (this.providerEmail) { - searchTerms.push({ field: 'email', op: 'ilike', value: this.providerEmail }); + searchTerms.push({ classes: ['acqpro','acqpc'], fields: ['email','email'], op: 'ilike', value: this.providerEmail }); } if (this.providerPhone) { - searchTerms.push({ field: 'phone', op: 'ilike', value: this.providerPhone }); + // this requires the flesh hash to contain: { ... join: { acqpc: { type: 'left' } } ... } + searchTerms.push({ classes: ['acqpc','acqpro','acqpro'], fields: ['phone','phone','fax_phone'], op: 'ilike', value: this.providerPhone }); } if (this.providerCurrencyType) { - searchTerms.push({ field: 'currency_type', op: 'ilike', value: this.providerCurrencyType }); + searchTerms.push({ fields: ['currency_type'], op: 'ilike', value: this.providerCurrencyType }); } if (this.providerSAN) { - searchTerms.push({ field: 'san', op: 'ilike', value: this.providerSAN }); + searchTerms.push({ fields: ['san'], op: 'ilike', value: this.providerSAN }); } if (this.providerEDIDefault) { - searchTerms.push({ field: 'edi_default', op: '=', value: this.providerEDIDefault }); + searchTerms.push({ fields: ['edi_default'], op: '=', value: this.providerEDIDefault }); } if (this.providerURL) { - searchTerms.push({ field: 'url', op: 'ilike', value: this.providerURL }); + searchTerms.push({ fields: ['url'], op: 'ilike', value: this.providerURL }); } if (this.providerIsActive) { - searchTerms.push({ field: 'active', op: '=', value: (this.providerIsActive ? 't' : 'f') }); + searchTerms.push({ fields: ['active'], op: '=', value: (this.providerIsActive ? 't' : 'f') }); } // tossing setTimeout here to ensure that the diff --git a/Open-ILS/src/eg2/src/app/staff/acq/provider/acq-provider-search.service.ts b/Open-ILS/src/eg2/src/app/staff/acq/provider/acq-provider-search.service.ts index 295f2c1051..501ce598f6 100644 --- a/Open-ILS/src/eg2/src/app/staff/acq/provider/acq-provider-search.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/acq/provider/acq-provider-search.service.ts @@ -8,7 +8,8 @@ import {Pager} from '@eg/share/util/pager'; import {EventService} from '@eg/core/event.service'; export interface AcqProviderSearchTerm { - field: string; + classes?: string[]; + fields: string[]; op: string; value: any; } @@ -36,6 +37,23 @@ export class AcqProviderSearchService { this.firstRun = false; } + generateSearchJoins(): any { + let joinPart = new Object(); + let class_list = new Array(); + + // get all the classes used + this._terms.forEach(term => {class_list = class_list.concat(term.classes)}) + + // filter out acqpro, empty names, and make unique + class_list = class_list.filter((x, i, a) => x && x !== 'acqpro' && a.indexOf(x) == i); + + // build a join clause for use in the "opts" part of a pcrud query + class_list.forEach(cls => { joinPart[cls].type = 'left' }) + + if (Object.keys(joinPart).length == 0) return null; + return joinPart; + } + generateSearch(filters): any { // base query to grab all providers const base = { id: { '!=': null } }; @@ -48,16 +66,58 @@ export class AcqProviderSearchService { return; } - const query_part = new Object(); + // not const because we may want an array + let query_part = new Object(); - query_part[term.field] = {}; let op = term.op; if (!op) { op = '=' }; // just in case + let val = term.value; if (op === 'ilike') { val = '%' + val + '%'; } - query_part[term.field][op] = val; + + let isOR = false; + term.fields.forEach( (field,ind) => { + const curr_cls = term.classes[ind]; + + if (ind == 1) { + // we're OR'ing in other classes/fields + // and this is the first so restructure + const first_cls = term.classes[0]; + isOR = true; + let tmp = new Object(); + if (first_cls) { + tmp['+'+first_cls] = query_part; + } else { + tmp = query_part; + } + + query_part = new Array(); + query_part.push(tmp); + } + + if (curr_cls) { + if (isOR) { + let tmp = new Object(); + tmp['+'+curr_cls][field][op] = val; + query_part.push(tmp); + } else { + query_part['+'+curr_cls][field][op] = val; + } + } else { + if (isOR) { + let tmp = new Object(); + tmp[field][op] = val; + query_part.push(tmp); + } else { + query_part[field][op] = val; + } + } + + }); + + if (isOR) { query_part = {'-or':query_part} } query.push(query_part); }); @@ -88,9 +148,11 @@ export class AcqProviderSearchService { return empty(); } + const joins = this.generateSearchJoins(); const query = this.generateSearch(gridSource.filters); const opts = {}; + if (joins) opts['join'] = joins; opts['offset'] = pager.offset; opts['limit'] = pager.limit; opts['au_by_id'] = true;