import {PcrudService} from '@eg/core/pcrud.service';
import {CatalogSearchContext} from './search-context';
import {RequestBodySearch, MatchQuery, MultiMatchQuery,
- Sort, BoolQuery, TermQuery} from 'elastic-builder';
+ Sort, NestedQuery, BoolQuery, TermQuery, RangeQuery} from 'elastic-builder';
@Injectable()
export class ElasticService {
) {}
init(): Promise<any> {
+
if (this.bibFields.length > 0) {
return Promise.resolve();
}
return false;
}
-
// For API consistency, returns an array of arrays whose first
// entry within each sub-array is a record ID.
performSearch(ctx: CatalogSearchContext): Promise<any> {
compileRequestBody(ctx: CatalogSearchContext): RequestBodySearch {
const search = new RequestBodySearch()
+
search.source(['id']); // only retrieve IDs
search.size(ctx.pager.limit)
search.from(ctx.pager.offset);
- const rootAnd = new BoolQuery();
+ const rootNode = new BoolQuery();
- this.compileTermSearch(ctx, rootAnd);
- this.addFilters(ctx, rootAnd);
+ if (ctx.termSearch.isSearchable()) {
+ this.addTermSearches(ctx, rootNode);
+ } else if (ctx.marcSearch.isSearchable()) {
+ this.addMarcSearches(ctx, rootNode);
+ }
+ this.addFilters(ctx, rootNode);
this.addSort(ctx, search);
- search.query(rootAnd);
+ search.query(rootNode);
return search;
}
if (!ctx.sort) { return; }
- // e.g. title, title.descending => [{title => 'desc'}]
+ // e.g. title, title.descending
const parts = ctx.sort.split(/\./);
search.sort(new Sort(parts[0], parts[1] ? 'desc' : 'asc'));
}
- addFilters(ctx: CatalogSearchContext, rootAnd: BoolQuery) {
+ addFilters(ctx: CatalogSearchContext, rootNode: BoolQuery) {
const ts = ctx.termSearch;
if (ts.format) {
- rootAnd.filter(new TermQuery(ts.formatCtype, ts.format));
+ rootNode.filter(new TermQuery(ts.formatCtype, ts.format));
}
Object.keys(ts.ccvmFilters).forEach(field => {
ts.ccvmFilters[field].forEach(value => {
if (value !== '') {
- rootAnd.filter(new TermQuery(field, value));
+ rootNode.filter(new TermQuery(field, value));
}
});
});
ts.facetFilters.forEach(f => {
if (f.facetValue !== '') {
- rootAnd.filter(new TermQuery(
+ rootNode.filter(new TermQuery(
`${f.facetClass}|${f.facetName}`, f.facetValue));
}
});
+
+ if (ts.date1 && ts.dateOp) {
+
+ if (ts.dateOp === 'is') {
+
+ rootNode.filter(new TermQuery('date1', ts.date1));
+
+ } else {
+
+ const range = new RangeQuery('date1');
+
+ switch (ts.dateOp) {
+ case 'before':
+ range.lt(ts.date1);
+ break;
+ case 'after':
+ range.gt(ts.date1);
+ break;
+ case 'between':
+ range.gt(ts.date1);
+ range.lt(ts.date2);
+ break;
+ }
+
+ rootNode.filter(range);
+ }
+ }
+ }
+
+ addMarcSearches(ctx: CatalogSearchContext, rootNode: BoolQuery) {
+ const ms = ctx.marcSearch;
+
+ ms.values.forEach((value, idx) => {
+ if (value === '' || value === null) { return; }
+
+ const marcQuery = new BoolQuery();
+ const tag = ms.tags[idx];
+ const subfield = ms.subfields[idx];
+
+ // Full-text search on the values
+ const valMatch = new MultiMatchQuery(['marc.value*'], value);
+ valMatch.operator('and');
+ valMatch.type('most_fields');
+ marcQuery.must(valMatch);
+
+ if (tag) {
+ marcQuery.must(new TermQuery('marc.tag', tag));
+ }
+
+ if (subfield) {
+ marcQuery.must(new TermQuery('marc.subfield', subfield));
+ }
+
+ rootNode.must(new NestedQuery(marcQuery, 'marc'));
+ });
}
- compileTermSearch(ctx: CatalogSearchContext, rootAnd: BoolQuery) {
+ addTermSearches(ctx: CatalogSearchContext, rootNode: BoolQuery) {
// TODO: boolean OR support.
const ts = ctx.termSearch;
ts.joinOp.forEach((op, idx) => {
+ const value = ts.query[idx];
+
const fieldClass = ts.fieldClass[idx];
const textIndex = `${fieldClass}|*text*`;
- const value = ts.query[idx];
let query;
switch (ts.matchOp[idx]) {
query = new MultiMatchQuery([textIndex], value);
query.operator('and');
query.type('most_fields');
- rootAnd.must(query);
+ rootNode.must(query);
break;
case 'phrase':
query = new MultiMatchQuery([textIndex], value);
query.type('phrase');
- rootAnd.must(query);
+ rootNode.must(query);
break;
case 'nocontains':
query = new MultiMatchQuery([textIndex], value);
query.operator('and');
query.type('most_fields');
- rootAnd.mustNot(query);
+ rootNode.mustNot(query);
break;
case 'exact':
case 'starts':
query = new MultiMatchQuery([textIndex], value);
query.type('phrase_prefix');
- rootAnd.must(query);
+ rootNode.must(query);
break;
}
});
}
-
-
}