diff --git a/src/server/saved_objects/service/lib/repository.js b/src/server/saved_objects/service/lib/repository.js index 6f3b4fffb21586..405521c75dc573 100644 --- a/src/server/saved_objects/service/lib/repository.js +++ b/src/server/saved_objects/service/lib/repository.js @@ -212,6 +212,7 @@ export class SavedObjectsRepository { * @property {string} [options.sortField] * @property {string} [options.sortOrder] * @property {Array} [options.fields] + * @property {Array} [options.filters] * @returns {promise} - { saved_objects: [{ id, type, version, attributes }], total, per_page, page } */ async find(options = {}) { @@ -224,6 +225,7 @@ export class SavedObjectsRepository { sortField, sortOrder, fields, + filters, } = options; if (searchFields && !Array.isArray(searchFields)) { @@ -247,7 +249,8 @@ export class SavedObjectsRepository { searchFields, type, sortField, - sortOrder + sortOrder, + filters, }) } }; diff --git a/src/server/saved_objects/service/lib/repository.test.js b/src/server/saved_objects/service/lib/repository.test.js index a02bfb9abb6194..b8c6a310cda5cb 100644 --- a/src/server/saved_objects/service/lib/repository.test.js +++ b/src/server/saved_objects/service/lib/repository.test.js @@ -388,13 +388,18 @@ describe('SavedObjectsRepository', () => { } }); - it('passes mappings, search, searchFields, type, sortField, and sortOrder to getSearchDsl', async () => { + it('passes mappings, search, searchFields, type, sortField, sortOrder, and filters to getSearchDsl', async () => { const relevantOpts = { search: 'foo*', searchFields: ['foo'], type: 'bar', sortField: 'name', sortOrder: 'desc', + filters: [{ + terms: { + type: ['foo', 'bar'] + } + }], }; await savedObjectsRepository.find(relevantOpts); diff --git a/src/server/saved_objects/service/lib/search_dsl/query_params.js b/src/server/saved_objects/service/lib/search_dsl/query_params.js index bcf62f21ef4158..e2f85973ca08e3 100644 --- a/src/server/saved_objects/service/lib/search_dsl/query_params.js +++ b/src/server/saved_objects/service/lib/search_dsl/query_params.js @@ -67,33 +67,43 @@ function getFieldsForTypes(searchFields, types) { * @param {Array} searchFields * @return {Object} */ -export function getQueryParams(mappings, type, search, searchFields) { - if (!type && !search) { - return {}; - } - - const bool = {}; +export function getQueryParams(mappings, type, search, searchFields, filters = []) { + const filter = [...filters]; + const must = []; if (type) { - bool.filter = [ - { [Array.isArray(type) ? 'terms' : 'term']: { type } } - ]; + filter.push({ [Array.isArray(type) ? 'terms' : 'term']: { type } }); } if (search) { - bool.must = [ - ...bool.must || [], - { - simple_query_string: { - query: search, - ...getFieldsForTypes( - searchFields, - getTypes(mappings, type) - ) - } + must.push({ + simple_query_string: { + query: search, + ...getFieldsForTypes( + searchFields, + getTypes(mappings, type) + ) } - ]; + }); + } + + if (filter.length === 0 && must.length === 0) { + return {}; + } + + const result = { + query: { + bool: {} + } + }; + + if (filter.length > 0) { + result.query.bool.filter = filter; + } + + if (must.length > 0) { + result.query.bool.must = must; } - return { query: { bool } }; + return result; } diff --git a/src/server/saved_objects/service/lib/search_dsl/query_params.test.js b/src/server/saved_objects/service/lib/search_dsl/query_params.test.js index 53b943ee6793bd..7b1a6f5dc7eb0d 100644 --- a/src/server/saved_objects/service/lib/search_dsl/query_params.test.js +++ b/src/server/saved_objects/service/lib/search_dsl/query_params.test.js @@ -95,6 +95,36 @@ describe('searchDsl/queryParams', () => { }); }); + describe('{type,filters}', () => { + it('includes filters and a term filter for type when type is a string', () => { + expect(getQueryParams(MAPPINGS, 'saved', null, null, [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { term: { type: 'saved' } } + ] + } + } + }); + }); + + it('includes filters and a terms filter for type when type is an array', () => { + expect(getQueryParams(MAPPINGS, ['saved', 'vis'], null, null, [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { terms: { type: ['saved', 'vis'] } } + ] + } + } + }); + }); + }); + describe('{search}', () => { it('includes just a sqs query', () => { expect(getQueryParams(MAPPINGS, null, 'us*')) @@ -115,8 +145,31 @@ describe('searchDsl/queryParams', () => { }); }); + describe('{search,filters}', () => { + it('includes filters and a sqs query', () => { + expect(getQueryParams(MAPPINGS, null, 'us*', null, [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } } + ], + must: [ + { + simple_query_string: { + query: 'us*', + all_fields: true + } + } + ] + } + } + }); + }); + }); + describe('{type,search}', () => { - it('includes bool with sqs query and term filter for type', () => { + it('includes bool with sqs query and term filter for type when type is a string', () => { expect(getQueryParams(MAPPINGS, 'saved', 'y*')) .toEqual({ query: { @@ -136,7 +189,7 @@ describe('searchDsl/queryParams', () => { } }); }); - it('includes bool with sqs query and terms filter for type', () => { + it('includes bool with sqs query and terms filter for type when type is an array', () => { expect(getQueryParams(MAPPINGS, ['saved', 'vis'], 'y*')) .toEqual({ query: { @@ -158,6 +211,52 @@ describe('searchDsl/queryParams', () => { }); }); + describe('{type,search,filters}', () => { + it('includes bool with sqs query, filters and term filter for type when type is a string', () => { + expect(getQueryParams(MAPPINGS, 'saved', 'y*', null, [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { term: { type: 'saved' } } + ], + must: [ + { + simple_query_string: { + query: 'y*', + all_fields: true + } + } + ] + } + } + }); + }); + + it('includes bool with sqs query, filters and terms filter for type when type is an array', () => { + expect(getQueryParams(MAPPINGS, ['saved', 'vis'], 'y*', null, [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { terms: { type: ['saved', 'vis'] } } + ], + must: [ + { + simple_query_string: { + query: 'y*', + all_fields: true + } + } + ] + } + } + }); + }); + }); + describe('{search,searchFields}', () => { it('includes all types for field', () => { expect(getQueryParams(MAPPINGS, null, 'y*', ['title'])) @@ -223,6 +322,80 @@ describe('searchDsl/queryParams', () => { }); }); + describe('{search,searchFields,filters}', () => { + it('includes all types for field and includes filter', () => { + expect(getQueryParams(MAPPINGS, null, 'y*', ['title'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'pending.title', + 'saved.title' + ] + } + } + ] + } + } + }); + }); + it('supports field boosting and includes filter', () => { + expect(getQueryParams(MAPPINGS, null, 'y*', ['title^3'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'pending.title^3', + 'saved.title^3' + ] + } + } + ] + } + } + }); + }); + it('supports field and multi-field and includes filter', () => { + expect(getQueryParams(MAPPINGS, null, 'y*', ['title', 'title.raw'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'pending.title', + 'saved.title', + 'pending.title.raw', + 'saved.title.raw', + ] + } + } + ] + } + } + }); + }); + }); + describe('{type,search,searchFields}', () => { it('includes bool, with term filter and sqs with field list', () => { expect(getQueryParams(MAPPINGS, 'saved', 'y*', ['title'])) @@ -315,4 +488,101 @@ describe('searchDsl/queryParams', () => { }); }); }); + + describe('{type,search,searchFields,filters}', () => { + it('includes bool, with term filter and filter and sqs with field list', () => { + expect(getQueryParams(MAPPINGS, 'saved', 'y*', ['title'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { term: { type: 'saved' } } + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'saved.title' + ] + } + } + ] + } + } + }); + }); + it('includes bool, with terms filter and filter and sqs with field list', () => { + expect(getQueryParams(MAPPINGS, ['saved', 'vis'], 'y*', ['title'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { terms: { type: ['saved', 'vis'] } } + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'saved.title', + 'vis.title' + ] + } + } + ] + } + } + }); + }); + it('supports fields pointing to multi-fields and filter', () => { + expect(getQueryParams(MAPPINGS, 'saved', 'y*', ['title.raw'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { term: { type: 'saved' } } + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'saved.title.raw' + ] + } + } + ] + } + } + }); + }); + it('supports multiple search fields and filter', () => { + expect(getQueryParams(MAPPINGS, 'saved', 'y*', ['title', 'title.raw'], [{ terms: { foo: ['bar', 'baz' ] } }])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { foo: ['bar', 'baz' ] } }, + { term: { type: 'saved' } } + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'saved.title', + 'saved.title.raw' + ] + } + } + ] + } + } + }); + }); + }); }); diff --git a/src/server/saved_objects/service/lib/search_dsl/search_dsl.js b/src/server/saved_objects/service/lib/search_dsl/search_dsl.js index ea34c127e98549..6843ebc306e1b2 100644 --- a/src/server/saved_objects/service/lib/search_dsl/search_dsl.js +++ b/src/server/saved_objects/service/lib/search_dsl/search_dsl.js @@ -28,7 +28,8 @@ export function getSearchDsl(mappings, options = {}) { search, searchFields, sortField, - sortOrder + sortOrder, + filters, } = options; if (!type && sortField) { @@ -40,7 +41,7 @@ export function getSearchDsl(mappings, options = {}) { } return { - ...getQueryParams(mappings, type, search, searchFields), + ...getQueryParams(mappings, type, search, searchFields, filters), ...getSortingParams(mappings, type, sortField, sortOrder), }; } diff --git a/src/server/saved_objects/service/lib/search_dsl/search_dsl.test.js b/src/server/saved_objects/service/lib/search_dsl/search_dsl.test.js index 85302b5e257222..b95f9f99671049 100644 --- a/src/server/saved_objects/service/lib/search_dsl/search_dsl.test.js +++ b/src/server/saved_objects/service/lib/search_dsl/search_dsl.test.js @@ -46,13 +46,16 @@ describe('getSearchDsl', () => { }); describe('passes control', () => { - it('passes (mappings, type, search, searchFields) to getQueryParams', () => { + it('passes (mappings, type, search, searchFields, filters) to getQueryParams', () => { const spy = sandbox.spy(queryParamsNS, 'getQueryParams'); const mappings = { type: { properties: {} } }; const opts = { type: 'foo', search: 'bar', searchFields: ['baz'], + filters: [ + { terms: { foo: ['bar', 'baz'] } } + ] }; getSearchDsl(mappings, opts); @@ -63,6 +66,7 @@ describe('getSearchDsl', () => { opts.type, opts.search, opts.searchFields, + opts.filters, ); });