Refactor SearchSource interface (#20334)

* Removed the dynamically assigned type, query, filter, sort, highlight, highlightAll, aggs, from, searchAfter, size, source, version, and fields methods.
* The accessor interface now consists of getField and setField methods which throw errors if an unrecognized property name is provided, in addition to getFields, setFields, getOwnField, and getId methods.
* Linked-list interface now consists of setParent and getParent.
* Factory interface now consists of create, createCopy, and createChild.
* Removed the unused unused enable, disable, and addFilterPredicate, and the redundant toString (method only used internally) and extend method (superseded by createChild).
* Internally, renamed the _state property to _data and grouped methods by concern.
This commit is contained in:
CJ Cenizal 2018-07-03 16:53:48 -07:00 committed by GitHub
parent e308828b83
commit e024d3dfa1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 627 additions and 662 deletions

View file

@ -21,16 +21,16 @@
export function createSearchSource(kbnApi, initialState, indexPattern, aggs, useTimeFilter, filters = []) {
const searchSource = new kbnApi.SearchSource(initialState);
// Do not not inherit from rootSearchSource to avoid picking up time and globals
searchSource.inherits(false);
searchSource.filter(() => {
searchSource.setParent(false);
searchSource.setField('filter', () => {
const activeFilters = [...filters];
if (useTimeFilter) {
activeFilters.push(kbnApi.timeFilter.createFilter(indexPattern));
}
return activeFilters;
});
searchSource.size(0);
searchSource.index(indexPattern);
searchSource.aggs(aggs);
searchSource.setField('size', 0);
searchSource.setField('index', indexPattern);
searchSource.setField('aggs', aggs);
return searchSource;
}

View file

@ -39,18 +39,17 @@ export function createSearchSourceStubProvider(hits, timeField) {
}),
};
searchSourceStub.filter = sinon.stub().returns(searchSourceStub);
searchSourceStub.inherits = sinon.stub().returns(searchSourceStub);
searchSourceStub.set = sinon.stub().returns(searchSourceStub);
searchSourceStub.get = sinon.spy(key => {
const previousSetCall = searchSourceStub.set.withArgs(key).lastCall;
searchSourceStub.setParent = sinon.stub().returns(searchSourceStub);
searchSourceStub.setField = sinon.stub().returns(searchSourceStub);
searchSourceStub.getField = sinon.spy(key => {
const previousSetCall = searchSourceStub.setField.withArgs(key).lastCall;
return previousSetCall ? previousSetCall.args[1] : null;
});
searchSourceStub.fetch = sinon.spy(() => {
const timeField = searchSourceStub._stubTimeField;
const lastQuery = searchSourceStub.set.withArgs('query').lastCall.args[1];
const lastQuery = searchSourceStub.setField.withArgs('query').lastCall.args[1];
const timeRange = lastQuery.query.constant_score.filter.range[timeField];
const lastSort = searchSourceStub.set.withArgs('sort').lastCall.args[1];
const lastSort = searchSourceStub.setField.withArgs('sort').lastCall.args[1];
const sortDirection = lastSort[0][timeField];
const sortFunction =
sortDirection === 'asc'

View file

@ -26,6 +26,24 @@ import { SearchSourceProvider } from 'ui/courier';
import { fetchAnchorProvider } from '../anchor';
function createSearchSourceStubProvider(hits) {
const searchSourceStub = {
_stubHits: hits,
};
searchSourceStub.setParent = sinon.stub().returns(searchSourceStub);
searchSourceStub.setField = sinon.stub().returns(searchSourceStub);
searchSourceStub.fetch = sinon.spy(() => Promise.resolve({
hits: {
hits: searchSourceStub._stubHits,
total: searchSourceStub._stubHits.length,
},
}));
return function SearchSourceStubProvider() {
return searchSourceStub;
};
}
describe('context app', function () {
beforeEach(ngMock.module('kibana'));
@ -61,9 +79,9 @@ describe('context app', function () {
return fetchAnchor('INDEX_PATTERN_ID', 'doc', 'id', [{ '@timestamp': 'desc' }, { '_doc': 'asc' }])
.then(() => {
const inheritsSpy = searchSourceStub.inherits;
expect(inheritsSpy.calledOnce).to.be(true);
expect(inheritsSpy.firstCall.args[0]).to.eql(false);
const setParentSpy = searchSourceStub.setParent;
expect(setParentSpy.calledOnce).to.be(true);
expect(setParentSpy.firstCall.args[0]).to.eql(false);
});
});
@ -72,9 +90,8 @@ describe('context app', function () {
return fetchAnchor('INDEX_PATTERN_ID', 'doc', 'id', [{ '@timestamp': 'desc' }, { '_doc': 'asc' }])
.then(() => {
const setIndexSpy = searchSourceStub.set.withArgs('index');
expect(setIndexSpy.calledOnce).to.be(true);
expect(setIndexSpy.firstCall.args[1]).to.eql({ id: 'INDEX_PATTERN_ID' });
const setFieldSpy = searchSourceStub.setField;
expect(setFieldSpy.firstCall.args[1]).to.eql({ id: 'INDEX_PATTERN_ID' });
});
});
@ -83,7 +100,7 @@ describe('context app', function () {
return fetchAnchor('INDEX_PATTERN_ID', 'doc', 'id', [{ '@timestamp': 'desc' }, { '_doc': 'asc' }])
.then(() => {
const setVersionSpy = searchSourceStub.set.withArgs('version');
const setVersionSpy = searchSourceStub.setField.withArgs('version');
expect(setVersionSpy.calledOnce).to.be(true);
expect(setVersionSpy.firstCall.args[1]).to.eql(true);
});
@ -94,7 +111,7 @@ describe('context app', function () {
return fetchAnchor('INDEX_PATTERN_ID', 'doc', 'id', [{ '@timestamp': 'desc' }, { '_doc': 'asc' }])
.then(() => {
const setSizeSpy = searchSourceStub.set.withArgs('size');
const setSizeSpy = searchSourceStub.setField.withArgs('size');
expect(setSizeSpy.calledOnce).to.be(true);
expect(setSizeSpy.firstCall.args[1]).to.eql(1);
});
@ -105,7 +122,7 @@ describe('context app', function () {
return fetchAnchor('INDEX_PATTERN_ID', 'doc', 'id', [{ '@timestamp': 'desc' }, { '_doc': 'asc' }])
.then(() => {
const setQuerySpy = searchSourceStub.set.withArgs('query');
const setQuerySpy = searchSourceStub.setField.withArgs('query');
expect(setQuerySpy.calledOnce).to.be(true);
expect(setQuerySpy.firstCall.args[1]).to.eql({
query: {
@ -128,7 +145,7 @@ describe('context app', function () {
return fetchAnchor('INDEX_PATTERN_ID', 'doc', 'id', [{ '@timestamp': 'desc' }, { '_doc': 'asc' }])
.then(() => {
const setSortSpy = searchSourceStub.set.withArgs('sort');
const setSortSpy = searchSourceStub.setField.withArgs('sort');
expect(setSortSpy.calledOnce).to.be(true);
expect(setSortSpy.firstCall.args[1]).to.eql([
{ '@timestamp': 'desc' },
@ -167,23 +184,3 @@ describe('context app', function () {
});
});
});
function createSearchSourceStubProvider(hits) {
const searchSourceStub = {
_stubHits: hits,
};
searchSourceStub.filter = sinon.stub().returns(searchSourceStub);
searchSourceStub.inherits = sinon.stub().returns(searchSourceStub);
searchSourceStub.set = sinon.stub().returns(searchSourceStub);
searchSourceStub.fetch = sinon.spy(() => Promise.resolve({
hits: {
hits: searchSourceStub._stubHits,
total: searchSourceStub._stubHits.length,
},
}));
return function SearchSourceStubProvider() {
return searchSourceStub;
};
}

View file

@ -95,7 +95,7 @@ describe('context app', function () {
[]
)
.then((hits) => {
const intervals = searchSourceStub.set.args
const intervals = searchSourceStub.setField.args
.filter(([property]) => property === 'query')
.map(([, { query }]) => _.get(query, ['constant_score', 'filter', 'range', '@timestamp']));
@ -131,7 +131,7 @@ describe('context app', function () {
[]
)
.then((hits) => {
const intervals = searchSourceStub.set.args
const intervals = searchSourceStub.setField.args
.filter(([property]) => property === 'query')
.map(([, { query }]) => _.get(query, ['constant_score', 'filter', 'range', '@timestamp']));
@ -177,9 +177,9 @@ describe('context app', function () {
[]
)
.then(() => {
const inheritsSpy = searchSourceStub.inherits;
expect(inheritsSpy.alwaysCalledWith(false)).to.be(true);
expect(inheritsSpy.called).to.be(true);
const setParentSpy = searchSourceStub.setParent;
expect(setParentSpy.alwaysCalledWith(false)).to.be(true);
expect(setParentSpy.called).to.be(true);
});
});
});

View file

@ -95,7 +95,7 @@ describe('context app', function () {
[]
)
.then((hits) => {
const intervals = searchSourceStub.set.args
const intervals = searchSourceStub.setField.args
.filter(([property]) => property === 'query')
.map(([, { query }]) => _.get(query, ['constant_score', 'filter', 'range', '@timestamp']));
@ -133,7 +133,7 @@ describe('context app', function () {
[]
)
.then((hits) => {
const intervals = searchSourceStub.set.args
const intervals = searchSourceStub.setField.args
.filter(([property]) => property === 'query')
.map(([, { query }]) => _.get(query, ['constant_score', 'filter', 'range', '@timestamp']));
@ -179,9 +179,9 @@ describe('context app', function () {
[]
)
.then(() => {
const inheritsSpy = searchSourceStub.inherits;
expect(inheritsSpy.alwaysCalledWith(false)).to.be(true);
expect(inheritsSpy.called).to.be(true);
const setParentSpy = searchSourceStub.setParent;
expect(setParentSpy.alwaysCalledWith(false)).to.be(true);
expect(setParentSpy.called).to.be(true);
});
});
});

View file

@ -32,11 +32,11 @@ export function fetchAnchorProvider(indexPatterns, Private) {
) {
const indexPattern = await indexPatterns.get(indexPatternId);
const searchSource = new SearchSource()
.inherits(false)
.set('index', indexPattern)
.set('version', true)
.set('size', 1)
.set('query', {
.setParent(false)
.setField('index', indexPattern)
.setField('version', true)
.setField('size', 1)
.setField('query', {
query: {
constant_score: {
filter: {
@ -49,7 +49,7 @@ export function fetchAnchorProvider(indexPatterns, Private) {
},
language: 'lucene',
})
.set('sort', sort);
.setField('sort', sort);
const response = await searchSource.fetch();

View file

@ -33,8 +33,8 @@ import { reverseSortDirection } from './utils/sorting';
/**
* @typedef {Object} SearchSourceT
* @prop {function(): Promise<SearchResult>} fetch
* @prop {function(string, any): SearchSourceT} set
* @prop {function(any): SearchSourceT} inherits
* @prop {function(string, any): SearchSourceT} setField
* @prop {function(any): SearchSourceT} setParent
*/
/**
@ -162,9 +162,9 @@ function fetchContextProvider(indexPatterns, Private) {
const indexPattern = await indexPatterns.get(indexPatternId);
return new SearchSource()
.inherits(false)
.set('index', indexPattern)
.set('filter', filters);
.setParent(false)
.setField('index', indexPattern)
.setField('filter', filters);
}
/**
@ -209,8 +209,8 @@ function fetchContextProvider(indexPatterns, Private) {
};
const response = await searchSource
.set('size', maxCount)
.set('query', {
.setField('size', maxCount)
.setField('query', {
query: {
constant_score: {
filter: {
@ -225,15 +225,15 @@ function fetchContextProvider(indexPatterns, Private) {
},
language: 'lucene'
})
.set('searchAfter', [
.setField('searchAfter', [
afterTimeValue !== null ? afterTimeValue : startTimeValue,
tieBreakerValue,
])
.set('sort', [
.setField('sort', [
{ [timeField]: timeSortDirection },
{ [tieBreakerField]: tieBreakerSortDirection },
])
.set('version', true)
.setField('version', true)
.fetch();
return response.hits ? response.hits.hits : [];

View file

@ -24,7 +24,7 @@ export function getSavedDashboardMock(config) {
title: 'my dashboard',
panelsJSON: '[]',
searchSource: {
getOwn: (param) => param
getOwnField: (param) => param
}
};
return Object.assign(defaults, config);

View file

@ -579,8 +579,8 @@ export class DashboardStateManager {
*/
applyFilters(query, filters) {
this.appState.query = query;
this.savedDashboard.searchSource.set('query', query);
this.savedDashboard.searchSource.set('filter', filters);
this.savedDashboard.searchSource.setField('query', query);
this.savedDashboard.searchSource.setField('filter', filters);
this.saveState();
}

View file

@ -44,7 +44,7 @@ export class FilterUtils {
* both query filters and filter bar filters.
*/
static getDashboardFilters(dashboard) {
return dashboard.searchSource.getOwn('filter');
return dashboard.searchSource.getOwnField('filter');
}
/**
@ -53,8 +53,8 @@ export class FilterUtils {
* @returns {QueryFilter}
*/
static getQueryFilterForDashboard(dashboard) {
if (dashboard.searchSource.getOwn('query')) {
return dashboard.searchSource.getOwn('query');
if (dashboard.searchSource.getOwnField('query')) {
return dashboard.searchSource.getOwnField('query');
}
const dashboardFilters = this.getDashboardFilters(dashboard);

View file

@ -196,18 +196,17 @@ function discoverController(
$scope.searchSource = savedSearch.searchSource;
$scope.indexPattern = resolveIndexPatternLoading();
$scope.searchSource
.set('index', $scope.indexPattern)
.highlightAll(true)
.version(true);
.setField('index', $scope.indexPattern)
.setField('highlightAll', true)
.setField('version', true);
// searchSource which applies time range
const timeRangeSearchSource = savedSearch.searchSource.new();
timeRangeSearchSource.set('filter', () => {
const timeRangeSearchSource = savedSearch.searchSource.create();
timeRangeSearchSource.setField('filter', () => {
return timefilter.createFilter($scope.indexPattern);
});
$scope.searchSource.inherits(timeRangeSearchSource);
$scope.searchSource.setParent(timeRangeSearchSource);
const pageTitleSuffix = savedSearch.id && savedSearch.title ? `: ${savedSearch.title}` : '';
docTitle.change(`Discover${pageTitleSuffix}`);
@ -258,26 +257,26 @@ function discoverController(
};
this.getSharingData = async () => {
const searchSource = $scope.searchSource.clone();
const searchSource = $scope.searchSource.createCopy();
const { searchFields, selectFields } = await getSharingDataFields();
searchSource.set('fields', searchFields);
searchSource.set('sort', getSort($state.sort, $scope.indexPattern));
searchSource.set('highlight', null);
searchSource.set('highlightAll', null);
searchSource.set('aggs', null);
searchSource.set('size', null);
searchSource.setField('fields', searchFields);
searchSource.setField('sort', getSort($state.sort, $scope.indexPattern));
searchSource.setField('highlight', null);
searchSource.setField('highlightAll', null);
searchSource.setField('aggs', null);
searchSource.setField('size', null);
const body = await searchSource.getSearchRequestBody();
return {
searchRequest: {
index: searchSource.get('index').title,
index: searchSource.getField('index').title,
body
},
fields: selectFields,
metaFields: $scope.indexPattern.metaFields,
conflictedTypesFields: $scope.indexPattern.fields.filter(f => f.type === 'conflict').map(f => f.name),
indexPatternId: searchSource.get('index').id
indexPatternId: searchSource.getField('index').id
};
};
@ -293,7 +292,7 @@ function discoverController(
function getStateDefaults() {
return {
query: $scope.searchSource.get('query') || {
query: $scope.searchSource.getField('query') || {
query: '',
language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage')
},
@ -301,7 +300,7 @@ function discoverController(
columns: savedSearch.columns.length > 0 ? savedSearch.columns : config.get('defaultColumns').slice(),
index: $scope.indexPattern.id,
interval: 'auto',
filters: _.cloneDeep($scope.searchSource.getOwn('filter'))
filters: _.cloneDeep($scope.searchSource.getOwnField('filter'))
};
}
@ -344,7 +343,7 @@ function discoverController(
if (!sort) return;
// get the current sort from {key: val} to ["key", "val"];
const currentSort = _.pairs($scope.searchSource.get('sort')).pop();
const currentSort = _.pairs($scope.searchSource.getField('sort')).pop();
// if the searchSource doesn't know, tell it so
if (!angular.equals(sort, currentSort)) $scope.fetch();
@ -535,7 +534,11 @@ function discoverController(
}
$scope.updateTime();
if (sort[0] === '_score') segmented.setMaxSegments(1);
if (sort[0] === '_score') {
segmented.setMaxSegments(1);
}
segmented.setDirection(sortBy === 'time' ? (sort[1] || 'desc') : 'desc');
segmented.setSortFn(sortFn);
segmented.setSize($scope.opts.sampleSize);
@ -567,14 +570,14 @@ function discoverController(
.resolve(responseHandler($scope.vis, merged))
.then(resp => {
$scope.visData = resp;
const visEl = $element.find('.visualization-container')[0];
const visEl = $element.find('#discoverHistogram')[0];
visualizationLoader(visEl, $scope.vis, $scope.visData, $scope.uiState, { listenOnChange: true });
});
}
$scope.hits = merged.hits.total;
const indexPattern = $scope.searchSource.get('index');
const indexPattern = $scope.searchSource.getField('index');
// the merge rows, use a new array to help watchers
$scope.rows = merged.hits.hits.slice();
@ -646,10 +649,10 @@ function discoverController(
$scope.updateDataSource = Promise.method(function updateDataSource() {
$scope.searchSource
.size($scope.opts.sampleSize)
.sort(getSort($state.sort, $scope.indexPattern))
.query(!$state.query ? null : $state.query)
.set('filter', queryFilter.getFilters());
.setField('size', $scope.opts.sampleSize)
.setField('sort', getSort($state.sort, $scope.indexPattern))
.setField('query', !$state.query ? null : $state.query)
.setField('filter', queryFilter.getFilters());
});
$scope.setSortOrder = function setSortOrder(columnName, direction) {
@ -731,7 +734,7 @@ function discoverController(
return $scope.vis.getAggConfig().onSearchRequestStart(searchSource, searchRequest);
});
$scope.searchSource.aggs(function () {
$scope.searchSource.setField('aggs', function () {
return $scope.vis.getAggConfig().toDsl();
});
}
@ -747,7 +750,7 @@ function discoverController(
const stateVal = props.stateVal;
const stateValFound = props.stateValFound;
const own = $scope.searchSource.getOwn('index');
const own = $scope.searchSource.getOwnField('index');
if (own && !stateVal) return own;
if (stateVal && !stateValFound) {

View file

@ -29,7 +29,7 @@ export class SearchEmbeddable extends Embeddable {
metadata: {
title: savedSearch.title,
editUrl,
indexPattern: savedSearch.searchSource.get('index')
indexPattern: savedSearch.searchSource.getField('index')
}
});
this.onEmbeddableStateChanged = onEmbeddableStateChanged;
@ -58,8 +58,8 @@ export class SearchEmbeddable extends Embeddable {
this.searchScope.sort = this.customization.sort || this.savedSearch.sort;
this.searchScope.sharedItemTitle = this.panelTitle;
this.filtersSearchSource.set('filter', this.filters);
this.filtersSearchSource.set('query', this.query);
this.filtersSearchSource.setField('filter', this.filters);
this.filtersSearchSource.setField('query', this.query);
}
onContainerStateChanged(containerState) {
@ -85,15 +85,15 @@ export class SearchEmbeddable extends Embeddable {
this.searchScope.description = this.savedSearch.description;
this.searchScope.searchSource = this.savedSearch.searchSource;
const timeRangeSearchSource = this.searchScope.searchSource.new();
timeRangeSearchSource.filter(() => {
return getTime(this.searchScope.searchSource.get('index'), this.timeRange);
const timeRangeSearchSource = this.searchScope.searchSource.create();
timeRangeSearchSource.setField('filter', () => {
return getTime(this.searchScope.searchSource.getField('index'), this.timeRange);
});
this.filtersSearchSource = this.searchScope.searchSource.new();
this.filtersSearchSource.inherits(timeRangeSearchSource);
this.filtersSearchSource = this.searchScope.searchSource.create();
this.filtersSearchSource.setParent(timeRangeSearchSource);
this.searchScope.searchSource.inherits(this.filtersSearchSource);
this.searchScope.searchSource.setParent(this.filtersSearchSource);
this.pushContainerStateParamsToScope();
@ -103,14 +103,14 @@ export class SearchEmbeddable extends Embeddable {
};
this.searchScope.addColumn = (columnName) => {
this.savedSearch.searchSource.get('index').popularizeField(columnName, 1);
this.savedSearch.searchSource.getField('index').popularizeField(columnName, 1);
columnActions.addColumn(this.searchScope.columns, columnName);
this.searchScope.columns = this.customization.columns = this.searchScope.columns;
this.emitEmbeddableStateChange(this.getEmbeddableState());
};
this.searchScope.removeColumn = (columnName) => {
this.savedSearch.searchSource.get('index').popularizeField(columnName, 1);
this.savedSearch.searchSource.getField('index').popularizeField(columnName, 1);
columnActions.removeColumn(this.searchScope.columns, columnName);
this.customization.columns = this.searchScope.columns;
this.emitEmbeddableStateChange(this.getEmbeddableState());
@ -123,7 +123,7 @@ export class SearchEmbeddable extends Embeddable {
};
this.searchScope.filter = (field, value, operator) => {
const index = this.savedSearch.searchSource.get('index').id;
const index = this.savedSearch.searchSource.getField('index').id;
const stagedFilter = {
field,
value,

View file

@ -48,7 +48,7 @@
columns="state.columns"
hits="rows"
field-counts="fieldCounts"
index-pattern="searchSource.get('index')"
index-pattern="searchSource.getField('index')"
index-pattern-list="opts.indexPatternList"
state="state"
on-add-field="addColumn"
@ -129,7 +129,7 @@
</header>
<div class="visualization-container"
<div id="discoverHistogram"
ng-if="vis && rows.length !== 0"
style="display: flex; height: 200px"
>

View file

@ -163,7 +163,7 @@ describe('Flyout', () => {
},
obj: {
searchSource: {
getOwn: () => 'MyIndexPattern*',
getOwnField: () => 'MyIndexPattern*',
},
},
},

View file

@ -144,7 +144,7 @@ export class Flyout extends Component {
);
const byId = groupBy(conflictedIndexPatterns, ({ obj }) =>
obj.searchSource.getOwn('index')
obj.searchSource.getOwnField('index')
);
const conflicts = Object.entries(byId).reduce(
(accum, [existingIndexPatternId, list]) => {

View file

@ -238,7 +238,7 @@ describe('resolveSavedObjects', () => {
{
obj: {
searchSource: {
getOwn: () => '1',
getOwnField: () => '1',
},
hydrateIndexPattern,
save,
@ -247,7 +247,7 @@ describe('resolveSavedObjects', () => {
{
obj: {
searchSource: {
getOwn: () => '3',
getOwnField: () => '3',
},
hydrateIndexPattern,
save,

View file

@ -81,7 +81,7 @@ export async function resolveIndexPatternConflicts(
) {
let importCount = 0;
await awaitEachItemInParallel(conflictedIndexPatterns, async ({ obj }) => {
let oldIndexId = obj.searchSource.getOwn('index');
let oldIndexId = obj.searchSource.getOwnField('index');
// Depending on the object, this can either be the raw id or the actual index pattern object
if (typeof oldIndexId !== 'string') {
oldIndexId = oldIndexId.id;

View file

@ -191,11 +191,11 @@ function VisEditor(
const stateDefaults = {
uiState: savedVis.uiStateJSON ? JSON.parse(savedVis.uiStateJSON) : {},
linked: !!savedVis.savedSearchId,
query: searchSource.getOwn('query') || {
query: searchSource.getOwnField('query') || {
query: '',
language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage')
},
filters: searchSource.getOwn('filter') || [],
filters: searchSource.getOwnField('filter') || [],
vis: savedVisState
};
@ -252,7 +252,7 @@ function VisEditor(
};
$scope.$watchMulti([
'searchSource.get("index")',
'searchSource.getField("index")',
'vis.type.options.showTimePicker',
], function ([index, requiresTimePicker]) {
const showTimeFilter = Boolean((!index || index.timeFieldName) && requiresTimePicker);
@ -279,8 +279,8 @@ function VisEditor(
// update the searchSource when query updates
$scope.fetch = function () {
$state.save();
savedVis.searchSource.set('query', $state.query);
savedVis.searchSource.set('filter', $state.filters);
savedVis.searchSource.setField('query', $state.query);
savedVis.searchSource.setField('filter', $state.filters);
$scope.vis.forceReload();
};
@ -348,23 +348,23 @@ function VisEditor(
toastNotifications.addSuccess(`Unlinked from saved search '${savedVis.savedSearch.title}'`);
$state.linked = false;
const parent = searchSource.getParent(true);
const parentsParent = parent.getParent(true);
const searchSourceParent = searchSource.getParent(true);
const searchSourceGrandparent = searchSourceParent.getParent(true);
delete savedVis.savedSearchId;
parent.set('filter', _.union(searchSource.getOwn('filter'), parent.getOwn('filter')));
searchSourceParent.setField('filter', _.union(searchSource.getOwnField('filter'), searchSourceParent.getOwnField('filter')));
// copy over all state except "aggs", "query" and "filter"
_(parent.toJSON())
_(searchSourceParent.toJSON())
.omit(['aggs', 'filter', 'query'])
.forOwn(function (val, key) {
searchSource.set(key, val);
searchSource.setField(key, val);
})
.commit();
$state.query = searchSource.get('query');
$state.filters = searchSource.get('filter');
searchSource.inherits(parentsParent);
$state.query = searchSource.getField('query');
$state.filters = searchSource.getField('filter');
searchSource.setParent(searchSourceGrandparent);
$scope.fetch();
};

View file

@ -95,7 +95,7 @@ uiModules
return self._getLinkedSavedSearch()
.then(function () {
self.searchSource.size(0);
self.searchSource.setField('size', 0);
return self.vis ? self._updateVis() : self._createVis();
});
@ -111,7 +111,7 @@ uiModules
}
if (self.savedSearch) {
self.searchSource.inherits(self.savedSearch.searchSource.getParent());
self.searchSource.setParent(self.savedSearch.searchSource.getParent());
self.savedSearch.destroy();
self.savedSearch = null;
}
@ -120,7 +120,7 @@ uiModules
return savedSearches.get(self.savedSearchId)
.then(function (savedSearch) {
self.savedSearch = savedSearch;
self.searchSource.inherits(self.savedSearch.searchSource);
self.searchSource.setParent(self.savedSearch.searchSource);
});
}
});
@ -137,7 +137,7 @@ uiModules
self.visState.title = self.title;
}
self.vis = new Vis(
self.searchSource.get('index'),
self.searchSource.getField('index'),
self.visState
);
@ -147,7 +147,7 @@ uiModules
SavedVis.prototype._updateVis = function () {
const self = this;
self.vis.indexPattern = self.searchSource.get('index');
self.vis.indexPattern = self.searchSource.getField('index');
self.visState.title = self.title;
self.vis.setState(self.visState);
};

View file

@ -22,15 +22,15 @@ export async function collectIndexPatterns(savedObjectsClient, panels) {
const { kibanaSavedObjectMeta, savedSearchId } = panel.attributes;
if (kibanaSavedObjectMeta && kibanaSavedObjectMeta.searchSourceJSON && !savedSearchId) {
let searchSource;
let searchSourceData;
try {
searchSource = JSON.parse(kibanaSavedObjectMeta.searchSourceJSON);
searchSourceData = JSON.parse(kibanaSavedObjectMeta.searchSourceJSON);
} catch (err) {
return acc;
}
if (searchSource.index && !acc.find(s => s.id === searchSource.index)) {
acc.push({ type: 'index-pattern', id: searchSource.index });
if (searchSourceData.index && !acc.find(s => s.id === searchSourceData.index)) {
acc.push({ type: 'index-pattern', id: searchSourceData.index });
}
}
return acc;

View file

@ -185,9 +185,9 @@ export function CoordinateMapsVisualizationProvider(Notifier, Private) {
async getGeohashBounds() {
const agg = this._getGeoHashAgg();
if (agg) {
const searchSource = this.vis.searchSource.makeChild();
searchSource.size(0);
searchSource.aggs(function () {
const searchSource = this.vis.searchSource.createChild();
searchSource.setField('size', 0);
searchSource.setField('aggs', function () {
const geoBoundsAgg = new AggConfig(agg.vis, {
type: 'geo_bounds',
enabled: true,

View file

@ -27,16 +27,15 @@ export default function stubSearchSource(Private, $q, Promise) {
let onResultsCount = 0;
return {
sort: sinon.spy(),
size: sinon.spy(),
setField: sinon.spy(),
fetch: sinon.spy(),
destroy: sinon.spy(),
get: function (param) {
getField: function (param) {
switch (param) {
case 'index':
return indexPattern;
default:
throw new Error('Param "' + param + '" is not implemented in the stubbed search source');
throw new Error(`Param "${param}" is not implemented in the stubbed search source`);
}
},
crankResults: function () {

View file

@ -70,9 +70,9 @@ export const histogramBucketAgg = new BucketAggType({
: { field: field.name };
return searchSource
.extend()
.size(0)
.aggs({
.createChild()
.setField('size', 0)
.setField('aggs', {
maxAgg: {
max: aggBody
},

View file

@ -93,10 +93,10 @@ export const termsBucketAgg = new BucketAggType({
},
createFilter: createFilterTerms,
postFlightRequest: async (resp, aggConfigs, aggConfig, searchSource) => {
const nestedSearchSource = searchSource.makeChild();
const nestedSearchSource = searchSource.createChild();
if (aggConfig.params.otherBucket) {
const filterAgg = buildOtherBucketAgg(aggConfigs, aggConfig, resp);
nestedSearchSource.set('aggs', filterAgg);
nestedSearchSource.setField('aggs', filterAgg);
const request = aggConfigs.vis.API.inspectorAdapters.requests.start('Other bucket', {
description: `This request counts the number of documents that fall

View file

@ -30,16 +30,16 @@ export function MergeDuplicatesRequestProvider(Private) {
return requests.map(function (req) {
if (!isRequest(req)) return req;
const iid = req.source._instanceid;
if (!index[iid]) {
const searchSourceId = req.source.getId();
if (!index[searchSourceId]) {
// this request is unique so far
index[iid] = req;
index[searchSourceId] = req;
// keep the request
return req;
}
// the source was requested at least twice
req._uniq = index[iid];
req._uniq = index[searchSourceId];
return DUPLICATE;
});
}

View file

@ -55,28 +55,28 @@ describe('SegmentedSearchRequest _createQueue', () => {
});
it('relies on indexPattern.toDetailedIndexList to generate queue', async function () {
const source = new MockSource();
const ip = source.get('index');
const searchSource = new MockSource();
const indexPattern = searchSource.getField('index');
const indices = [1, 2, 3];
sinon.stub(ip, 'toDetailedIndexList').returns(Promise.resolve(indices));
sinon.stub(indexPattern, 'toDetailedIndexList').returns(Promise.resolve(indices));
const req = new SegmentedSearchRequest({ source, errorHandler: () => {} });
const req = new SegmentedSearchRequest({ source: searchSource, errorHandler: () => {} });
const output = await req._createQueue();
expect(output).to.equal(indices);
});
it('tells the index pattern its direction', async function () {
const source = new MockSource();
const ip = source.get('index');
const req = new SegmentedSearchRequest({ source, errorHandler: () => {} });
sinon.stub(ip, 'toDetailedIndexList').returns(Promise.resolve([1, 2, 3]));
const searchSource = new MockSource();
const indexPattern = searchSource.getField('index');
const req = new SegmentedSearchRequest({ source: searchSource, errorHandler: () => {} });
sinon.stub(indexPattern, 'toDetailedIndexList').returns(Promise.resolve([1, 2, 3]));
req.setDirection('asc');
await req._createQueue();
expect(ip.toDetailedIndexList.lastCall.args[2]).to.be('asc');
expect(indexPattern.toDetailedIndexList.lastCall.args[2]).to.be('asc');
req.setDirection('desc');
await req._createQueue();
expect(ip.toDetailedIndexList.lastCall.args[2]).to.be('desc');
expect(indexPattern.toDetailedIndexList.lastCall.args[2]).to.be('desc');
});
});

View file

@ -50,8 +50,8 @@ describe('SegmentedSearchRequest index selection', function () {
}));
it('queries with size until all 500 docs returned', async function () {
const search = new MockSource();
const indexPattern = search.get('index');
const searchSource = new MockSource();
const indexPattern = searchSource.getField('index');
sinon.stub(indexPattern, 'toDetailedIndexList').returns(Promise.resolve([
{ index: 'one', min: 0, max: 1 },
{ index: 'two', min: 0, max: 1 },
@ -60,7 +60,7 @@ describe('SegmentedSearchRequest index selection', function () {
{ index: 'five', min: 0, max: 1 },
]));
const req = new SegmentedSearchRequest({ source: search, errorHandler: () => {} });
const req = new SegmentedSearchRequest({ source: searchSource, errorHandler: () => {} });
req._handle.setDirection('desc');
req._handle.setSortFn(new HitSortFn('desc'));
req._handle.setSize(500);
@ -96,8 +96,8 @@ describe('SegmentedSearchRequest index selection', function () {
});
it(`sets size 0 for indices that couldn't preclude hits`, async function () {
const search = new MockSource();
const indexPattern = search.get('index');
const searchSource = new MockSource();
const indexPattern = searchSource.getField('index');
// the segreq is looking for 10 documents, and we will give it ten docs with time:5 in the first response.
// on the second index it should still request 10 documents because it could produce documents with time:5.
@ -111,7 +111,7 @@ describe('SegmentedSearchRequest index selection', function () {
{ index: 'five', min: 5, max: 50 },
]));
const req = new SegmentedSearchRequest({ source: search, errorHandler: () => {} });
const req = new SegmentedSearchRequest({ source: searchSource, errorHandler: () => {} });
req._handle.setDirection('desc');
req._handle.setSortFn(new HitSortFn('desc'));
req._handle.setSize(10);

View file

@ -195,7 +195,7 @@ export function SegmentedSearchRequestProvider(Private, config) {
_createQueue() {
const timeBounds = timefilter.getBounds();
const indexPattern = this.source.get('index');
const indexPattern = this.source.getField('index');
this._queueCreated = false;
return indexPattern.toDetailedIndexList(timeBounds.min, timeBounds.max, this._direction)
@ -298,7 +298,7 @@ export function SegmentedSearchRequestProvider(Private, config) {
_detectHitsWindow(hits) {
hits = hits || [];
const indexPattern = this.source.get('index');
const indexPattern = this.source.getField('index');
const desiredSize = this._desiredSize;
const size = _.size(hits);

View file

@ -432,11 +432,11 @@ describe('Saved Object', function () {
});
const savedObject = new SavedObject(config);
expect(!!savedObject.searchSource.get('index')).to.be(false);
expect(!!savedObject.searchSource.getField('index')).to.be(false);
return savedObject.init().then(() => {
expect(afterESRespCallback.called).to.be(true);
const index = savedObject.searchSource.get('index');
const index = savedObject.searchSource.getField('index');
expect(index instanceof IndexPattern).to.be(true);
expect(index.id).to.equal(indexPatternId);
});

View file

@ -112,23 +112,23 @@ export function SavedObjectProvider(Promise, Private, Notifier, confirmModalProm
const parseSearchSource = (searchSourceJson) => {
if (!this.searchSource) return;
// if we have a searchSource, set its state based on the searchSourceJSON field
let state;
// if we have a searchSource, set its values based on the searchSourceJson field
let searchSourceValues;
try {
state = JSON.parse(searchSourceJson);
searchSourceValues = JSON.parse(searchSourceJson);
} catch (e) {
state = {};
searchSourceValues = {};
}
const oldState = this.searchSource.toJSON();
const fnProps = _.transform(oldState, function (dynamic, val, name) {
const searchSourceFields = this.searchSource.getFields();
const fnProps = _.transform(searchSourceFields, function (dynamic, val, name) {
if (_.isFunction(val)) dynamic[name] = val;
}, {});
this.searchSource.set(_.defaults(state, fnProps));
this.searchSource.setFields(_.defaults(searchSourceValues, fnProps));
if (!_.isUndefined(this.searchSource.getOwn('query'))) {
this.searchSource.set('query', migrateLegacyQuery(this.searchSource.getOwn('query')));
if (!_.isUndefined(this.searchSource.getOwnField('query'))) {
this.searchSource.setField('query', migrateLegacyQuery(this.searchSource.getOwnField('query')));
}
};
@ -144,11 +144,11 @@ export function SavedObjectProvider(Promise, Private, Notifier, confirmModalProm
}
if (config.clearSavedIndexPattern) {
this.searchSource.set('index', undefined);
this.searchSource.setField('index', null);
return Promise.resolve(null);
}
let index = id || config.indexPattern || this.searchSource.getOwn('index');
let index = id || config.indexPattern || this.searchSource.getOwnField('index');
if (!index) {
return Promise.resolve(null);
@ -162,7 +162,7 @@ export function SavedObjectProvider(Promise, Private, Notifier, confirmModalProm
// At this point index will either be an IndexPattern, if cached, or a promise that
// will return an IndexPattern, if not cached.
return Promise.resolve(index).then(indexPattern => {
this.searchSource.set('index', indexPattern);
this.searchSource.setField('index', indexPattern);
});
};
@ -260,8 +260,9 @@ export function SavedObjectProvider(Promise, Private, Notifier, confirmModalProm
});
if (this.searchSource) {
const searchSourceFields = _.omit(this.searchSource.getFields(), ['sort', 'size']);
body.kibanaSavedObjectMeta = {
searchSourceJSON: angular.toJson(_.omit(this.searchSource.toJSON(), ['sort', 'size']))
searchSourceJSON: angular.toJson(searchSourceFields)
};
}

View file

@ -54,18 +54,18 @@ describe('SearchSource', function () {
describe('#onResults()', function () {
it('adds a request to the searchRequestQueue', function () {
const source = new SearchSource();
const searchSource = new SearchSource();
expect(searchRequestQueue.getCount()).to.be(0);
source.onResults();
searchSource.onResults();
expect(searchRequestQueue.getCount()).to.be(1);
});
it('returns a promise that is resolved with the results', function () {
const source = new SearchSource();
const searchSource = new SearchSource();
const fakeResults = {};
const promise = source.onResults().then((results) => {
const promise = searchSource.onResults().then((results) => {
expect(results).to.be(fakeResults);
});
@ -77,106 +77,132 @@ describe('SearchSource', function () {
describe('#destroy()', function () {
it('aborts all startable requests', function () {
const source = new SearchSource();
source.onResults();
const searchSource = new SearchSource();
searchSource.onResults();
const searchRequest = searchRequestQueue.getSearchRequestAt(0);
sinon.stub(searchRequest, 'canStart').returns(true);
source.destroy();
searchSource.destroy();
expect(searchRequestQueue.getCount()).to.be(0);
});
it('aborts all non-startable requests', function () {
const source = new SearchSource();
source.onResults();
const searchSource = new SearchSource();
searchSource.onResults();
const searchRequest = searchRequestQueue.getSearchRequestAt(0);
sinon.stub(searchRequest, 'canStart').returns(false);
source.destroy();
searchSource.destroy();
expect(searchRequestQueue.getCount()).to.be(0);
});
});
describe('#index()', function () {
describe('#setField()', function () {
it('sets the value for the property', function () {
const searchSource = new SearchSource();
searchSource.setField('aggs', 5);
expect(searchSource.getField('aggs')).to.be(5);
});
it('throws an error if the property is not accepted', function () {
const searchSource = new SearchSource();
expect(() => searchSource.setField('index', 5)).to.throwError();
});
});
describe('#getField()', function () {
it('gets the value for the property', function () {
const searchSource = new SearchSource();
searchSource.setField('aggs', 5);
expect(searchSource.getField('aggs')).to.be(5);
});
it('throws an error if the property is not accepted', function () {
const searchSource = new SearchSource();
expect(() => searchSource.getField('unacceptablePropName')).to.throwError();
});
});
describe(`#setField('index')`, function () {
describe('auto-sourceFiltering', function () {
describe('new index pattern assigned', function () {
it('generates a source filter', function () {
const source = new SearchSource();
expect(source.get('index')).to.be(undefined);
expect(source.get('source')).to.be(undefined);
source.set('index', indexPattern);
expect(source.get('index')).to.be(indexPattern);
expect(source.get('source')).to.be.a('function');
it('generates a searchSource filter', function () {
const searchSource = new SearchSource();
expect(searchSource.getField('index')).to.be(undefined);
expect(searchSource.getField('source')).to.be(undefined);
searchSource.setField('index', indexPattern);
expect(searchSource.getField('index')).to.be(indexPattern);
expect(searchSource.getField('source')).to.be.a('function');
});
it('removes created source filter on removal', function () {
const source = new SearchSource();
source.set('index', indexPattern);
source.set('index', null);
expect(source.get('index')).to.be(undefined);
expect(source.get('source')).to.be(undefined);
it('removes created searchSource filter on removal', function () {
const searchSource = new SearchSource();
searchSource.setField('index', indexPattern);
searchSource.setField('index', null);
expect(searchSource.getField('index')).to.be(undefined);
expect(searchSource.getField('source')).to.be(undefined);
});
});
describe('new index pattern assigned over another', function () {
it('replaces source filter with new', function () {
const source = new SearchSource();
source.set('index', indexPattern);
const sourceFilter1 = source.get('source');
source.set('index', indexPattern2);
expect(source.get('index')).to.be(indexPattern2);
expect(source.get('source')).to.be.a('function');
expect(source.get('source')).to.not.be(sourceFilter1);
it('replaces searchSource filter with new', function () {
const searchSource = new SearchSource();
searchSource.setField('index', indexPattern);
const searchSourceFilter1 = searchSource.getField('source');
searchSource.setField('index', indexPattern2);
expect(searchSource.getField('index')).to.be(indexPattern2);
expect(searchSource.getField('source')).to.be.a('function');
expect(searchSource.getField('source')).to.not.be(searchSourceFilter1);
});
it('removes created source filter on removal', function () {
const source = new SearchSource();
source.set('index', indexPattern);
source.set('index', indexPattern2);
source.set('index', null);
expect(source.get('index')).to.be(undefined);
expect(source.get('source')).to.be(undefined);
it('removes created searchSource filter on removal', function () {
const searchSource = new SearchSource();
searchSource.setField('index', indexPattern);
searchSource.setField('index', indexPattern2);
searchSource.setField('index', null);
expect(searchSource.getField('index')).to.be(undefined);
expect(searchSource.getField('source')).to.be(undefined);
});
});
describe('ip assigned before custom source filter', function () {
it('custom source filter becomes new source', function () {
const source = new SearchSource();
describe('ip assigned before custom searchSource filter', function () {
it('custom searchSource filter becomes new searchSource', function () {
const searchSource = new SearchSource();
const football = {};
source.set('index', indexPattern);
expect(source.get('source')).to.be.a('function');
source.set('source', football);
expect(source.get('index')).to.be(indexPattern);
expect(source.get('source')).to.be(football);
searchSource.setField('index', indexPattern);
expect(searchSource.getField('source')).to.be.a('function');
searchSource.setField('source', football);
expect(searchSource.getField('index')).to.be(indexPattern);
expect(searchSource.getField('source')).to.be(football);
});
it('custom source stays after removal', function () {
const source = new SearchSource();
it('custom searchSource stays after removal', function () {
const searchSource = new SearchSource();
const football = {};
source.set('index', indexPattern);
source.set('source', football);
source.set('index', null);
expect(source.get('index')).to.be(undefined);
expect(source.get('source')).to.be(football);
searchSource.setField('index', indexPattern);
searchSource.setField('source', football);
searchSource.setField('index', null);
expect(searchSource.getField('index')).to.be(undefined);
expect(searchSource.getField('source')).to.be(football);
});
});
describe('ip assigned after custom source filter', function () {
describe('ip assigned after custom searchSource filter', function () {
it('leaves the custom filter in place', function () {
const source = new SearchSource();
const searchSource = new SearchSource();
const football = {};
source.set('source', football);
source.set('index', indexPattern);
expect(source.get('index')).to.be(indexPattern);
expect(source.get('source')).to.be(football);
searchSource.setField('source', football);
searchSource.setField('index', indexPattern);
expect(searchSource.getField('index')).to.be(indexPattern);
expect(searchSource.getField('source')).to.be(football);
});
it('custom source stays after removal', function () {
const source = new SearchSource();
it('custom searchSource stays after removal', function () {
const searchSource = new SearchSource();
const football = {};
source.set('source', football);
source.set('index', indexPattern);
source.set('index', null);
expect(source.get('index')).to.be(undefined);
expect(source.get('source')).to.be(football);
searchSource.setField('source', football);
searchSource.setField('index', indexPattern);
searchSource.setField('index', null);
expect(searchSource.getField('index')).to.be(undefined);
expect(searchSource.getField('source')).to.be(football);
});
});
});
@ -184,66 +210,66 @@ describe('SearchSource', function () {
describe('#onRequestStart()', () => {
it('should be called when starting a request', async () => {
const source = new SearchSource();
const searchSource = new SearchSource();
const fn = sinon.spy();
source.onRequestStart(fn);
searchSource.onRequestStart(fn);
const request = {};
source.requestIsStarting(request);
searchSource.requestIsStarting(request);
await timeout();
expect(fn.calledWith(source, request)).to.be(true);
expect(fn.calledWith(searchSource, request)).to.be(true);
});
it('should not be called on parent searchSource', async () => {
const parent = new SearchSource();
const source = new SearchSource().inherits(parent);
const searchSource = new SearchSource().setParent(parent);
const fn = sinon.spy();
source.onRequestStart(fn);
searchSource.onRequestStart(fn);
const parentFn = sinon.spy();
parent.onRequestStart(parentFn);
const request = {};
source.requestIsStarting(request);
searchSource.requestIsStarting(request);
await timeout();
expect(fn.calledWith(source, request)).to.be(true);
expect(fn.calledWith(searchSource, request)).to.be(true);
expect(parentFn.notCalled).to.be(true);
});
it('should be called on parent searchSource if callParentStartHandlers is true', async () => {
const parent = new SearchSource();
const source = new SearchSource().inherits(parent, { callParentStartHandlers: true });
const searchSource = new SearchSource().setParent(parent, { callParentStartHandlers: true });
const fn = sinon.spy();
source.onRequestStart(fn);
searchSource.onRequestStart(fn);
const parentFn = sinon.spy();
parent.onRequestStart(parentFn);
const request = {};
source.requestIsStarting(request);
searchSource.requestIsStarting(request);
await timeout();
expect(fn.calledWith(source, request)).to.be(true);
expect(parentFn.calledWith(source, request)).to.be(true);
expect(fn.calledWith(searchSource, request)).to.be(true);
expect(parentFn.calledWith(searchSource, request)).to.be(true);
});
});
describe('#_mergeProp', function () {
describe('filter', function () {
let source;
let searchSource;
let state;
beforeEach(function () {
source = new SearchSource();
searchSource = new SearchSource();
state = {};
});
[null, undefined].forEach(falsyValue => {
it(`ignores ${falsyValue} filter`, function () {
source._mergeProp(state, falsyValue, 'filter');
searchSource._mergeProp(state, falsyValue, 'filter');
expect(state.filters).to.be(undefined);
});
});
[false, 0, '', NaN].forEach(falsyValue => {
it(`doesn't add ${falsyValue} filter`, function () {
source._mergeProp(state, falsyValue, 'filter');
searchSource._mergeProp(state, falsyValue, 'filter');
expect(state.filters).to.be.empty();
});
});
@ -252,7 +278,7 @@ describe('SearchSource', function () {
const filter = {
meta: {}
};
source._mergeProp(state, filter, 'filter');
searchSource._mergeProp(state, filter, 'filter');
expect(state.filters).to.eql([filter]);
});
@ -262,7 +288,7 @@ describe('SearchSource', function () {
disabled: false
}
};
source._mergeProp(state, filter, 'filter');
searchSource._mergeProp(state, filter, 'filter');
expect(state.filters).to.eql([filter]);
});
@ -272,7 +298,7 @@ describe('SearchSource', function () {
disabled: true
}
};
source._mergeProp(state, filter, 'filter');
searchSource._mergeProp(state, filter, 'filter');
expect(state.filters).to.be.empty();
});
@ -289,7 +315,7 @@ describe('SearchSource', function () {
byName: {}
}
};
source._mergeProp(state, filter, 'filter');
searchSource._mergeProp(state, filter, 'filter');
expect(state.filters).to.eql([ filter ]);
});
});
@ -307,7 +333,7 @@ describe('SearchSource', function () {
byName: {}
}
};
source._mergeProp(state, filter, 'filter');
searchSource._mergeProp(state, filter, 'filter');
expect(state.filters).to.be.empty();
});
@ -325,20 +351,10 @@ describe('SearchSource', function () {
}
}
};
source._mergeProp(state, filter, 'filter');
searchSource._mergeProp(state, filter, 'filter');
expect(state.filters).to.eql([ filter ]);
});
});
it('uses custom filter predicate', function () {
source.addFilterPredicate(() => {
return false;
});
const filter = {};
source._mergeProp(state, filter, 'filter');
expect(state.filters).to.be.empty();
});
});
});
});

View file

@ -84,14 +84,31 @@ import { FieldWildcardProvider } from '../../field_wildcard';
import { getHighlightRequest } from '../../../../core_plugins/kibana/common/highlight';
import { BuildESQueryProvider } from './build_query';
function parseInitialState(initialState) {
if (!initialState) {
const FIELDS = [
'type',
'query',
'filter',
'sort',
'highlight',
'highlightAll',
'aggs',
'from',
'searchAfter',
'size',
'source',
'version',
'fields',
'index',
];
function parseInitialFields(initialFields) {
if (!initialFields) {
return {};
}
return typeof initialState === 'string' ?
JSON.parse(initialState)
: _.cloneDeep(initialState);
return typeof initialFields === 'string' ?
JSON.parse(initialFields)
: _.cloneDeep(initialFields);
}
function isIndexPattern(val) {
@ -110,45 +127,11 @@ export function SearchSourceProvider(Promise, Private, config) {
const forIp = Symbol('for which index pattern?');
class SearchSource {
constructor(initialState) {
this._instanceid = _.uniqueId('data_source');
constructor(initialFields) {
this._id = _.uniqueId('data_source');
this._state = parseInitialState(initialState);
/**
* List of the editable state properties that turn into a
* chainable API
*
* @type {Array}
*/
this._methods = [
'type',
'query',
'filter',
'sort',
'highlight',
'highlightAll',
'aggs',
'from',
'searchAfter',
'size',
'source',
'version',
'fields'
];
// set internal state values
this._methods.forEach(name => {
this[name] = val => {
if (val == null) {
delete this._state[name];
} else {
this._state[name] = val;
}
return this;
};
});
this._fields = parseInitialFields(initialFields);
this._parent = undefined;
this.history = [];
this._requestStartHandlers = [];
@ -163,13 +146,13 @@ export function SearchSourceProvider(Promise, Private, config) {
const disabled = _.get(filter, 'meta.disabled');
return disabled === undefined || disabled === false;
},
(filter, state) => {
(filter, data) => {
if (!config.get('courier:ignoreFilterIfFieldNotInIndex')) {
return true;
}
if ('meta' in filter && 'index' in state) {
const field = state.index.fields.byName[filter.meta.key];
if ('meta' in filter && 'index' in data) {
const field = data.index.fields.byName[filter.meta.key];
if (!field) return false;
}
return true;
@ -181,42 +164,125 @@ export function SearchSourceProvider(Promise, Private, config) {
* PUBLIC API
*****/
index(indexPattern) {
const state = this._state;
const hasSource = state.source;
const sourceCameFromIp = hasSource && state.source.hasOwnProperty(forIp);
const sourceIsForOurIp = sourceCameFromIp && state.source[forIp] === state.index;
if (sourceIsForOurIp) {
delete state.source;
}
if (indexPattern === undefined) return state.index;
if (indexPattern === null) return delete state.index;
if (!isIndexPattern(indexPattern)) {
throw new TypeError('expected indexPattern to be an IndexPattern duck.');
}
state.index = indexPattern;
if (!state.source) {
// imply source filtering based on the index pattern, but allow overriding
// it by simply setting another value for "source". When index is changed
state.source = function () {
return indexPattern.getSourceFiltering();
};
state.source[forIp] = indexPattern;
}
setFields(newFields) {
this._fields = newFields;
return this;
}
setField = (field, value) => {
if (!FIELDS.includes(field)) {
throw new Error(`Can't set field '${field}' on SearchSource. Acceptable fields are: ${FIELDS.join(', ')}.`);
}
if (field === 'index') {
const fields = this._fields;
const hasSource = fields.source;
const sourceCameFromIp = hasSource && fields.source.hasOwnProperty(forIp);
const sourceIsForOurIp = sourceCameFromIp && fields.source[forIp] === fields.index;
if (sourceIsForOurIp) {
delete fields.source;
}
if (value === null || value === undefined) {
delete fields.index;
return this;
}
if (!isIndexPattern(value)) {
throw new TypeError('expected indexPattern to be an IndexPattern duck.');
}
fields[field] = value;
if (!fields.source) {
// imply source filtering based on the index pattern, but allow overriding
// it by simply setting another field for "source". When index is changed
fields.source = function () {
return value.getSourceFiltering();
};
fields.source[forIp] = value;
}
return this;
}
if (value == null) {
delete this._fields[field];
return this;
}
this._fields[field] = value;
return this;
};
getId() {
return this._id;
}
getFields() {
return _.clone(this._fields);
}
/**
* Get fields from the fields
*/
getField = field => {
if (!FIELDS.includes(field)) {
throw new Error(`Can't get field '${field}' from SearchSource. Acceptable fields are: ${FIELDS.join(', ')}.`);
}
let searchSource = this;
while (searchSource) {
const value = searchSource._fields[field];
if (value !== void 0) {
return value;
}
searchSource = searchSource.getParent();
}
};
/**
* Get the field from our own fields, don't traverse up the chain
*/
getOwnField(field) {
if (!FIELDS.includes(field)) {
throw new Error(`Can't get field '${field}' from SearchSource. Acceptable fields are: ${FIELDS.join(', ')}.`);
}
const value = this._fields[field];
if (value !== void 0) {
return value;
}
}
create() {
return new SearchSource();
}
createCopy() {
const json = angular.toJson(this._fields);
const newSearchSource = new SearchSource(json);
// when serializing the internal fields we lose the internal classes used in the index
// pattern, so we have to set it again to workaround this behavior
newSearchSource.setField('index', this.getField('index'));
newSearchSource.setParent(this.getParent());
return newSearchSource;
}
createChild(options = {}) {
const childSearchSource = new SearchSource();
childSearchSource.setParent(this, options);
return childSearchSource;
}
/**
* Set a searchSource that this source should inherit from
* @param {SearchSource} searchSource - the parent searchSource
* @return {this} - chainable
*/
inherits(parent, options = {}) {
setParent(parent, options = {}) {
this._parent = parent;
this._inheritOptions = options;
return this;
@ -230,163 +296,6 @@ export function SearchSourceProvider(Promise, Private, config) {
return this._parent || undefined;
}
/**
* Temporarily prevent this Search from being fetched... not a fan but it's easy
*/
disable() {
this._fetchDisabled = true;
}
/**
* Reverse of SearchSource#disable(), only need to call this if source was previously disabled
*/
enable() {
this._fetchDisabled = false;
}
onBeginSegmentedFetch(initFunction) {
const self = this;
return new Promise((resolve, reject) => {
function addRequest() {
const defer = Promise.defer();
const errorHandler = (request, error) => {
reject(error);
request.abort();
};
const req = new SegmentedSearchRequest({ source: self, defer, errorHandler, initFn: initFunction });
// Return promises created by the completion handler so that
// errors will bubble properly
return req.getCompletePromise().then(addRequest);
}
addRequest();
});
}
addFilterPredicate(predicate) {
this._filterPredicates.push(predicate);
}
clone() {
const clone = new SearchSource(this.toString());
// when serializing the internal state with .toString() we lose the internal classes used in the index
// pattern, so we have to set it again to workaround this behavior
clone.set('index', this.get('index'));
clone.inherits(this.getParent());
return clone;
}
makeChild(params) {
return new SearchSource().inherits(this, params);
}
new() {
return new SearchSource();
}
async getSearchRequestBody() {
const searchRequest = await this._flatten();
return searchRequest.body;
}
/**
* Called by requests of this search source when they are done
* @param {Courier.Request} request
* @return {undefined}
*/
requestIsStopped() {
this.activeFetchCount -= 1;
}
/**
* Get values from the state
* @param {string} name - The name of the property desired
* @return {any} - the value found
*/
get(name) {
let self = this;
while (self) {
if (self._state[name] !== void 0) return self._state[name];
self = self.getParent();
}
}
/**
* Get the value from our own state, don't traverse up the chain
* @param {string} name - The name of the property desired
* @return {any} - the value found
*/
getOwn(name) {
if (this._state[name] !== void 0) return this._state[name];
}
/**
* Change the entire state of a SearchSource
* @param {object|string} state - The SearchSource's new state, or a
* string of the state value to set
*/
set(state, val) {
const self = this;
if (typeof state === 'string') {
// the getter and setter methods check for undefined explicitly
// to identify getters and null to identify deletion
if (val === undefined) {
val = null;
}
self[state](val);
} else {
self._state = state;
}
return self;
}
/**
* Create a new dataSource object of the same type
* as this, which inherits this dataSource's properties
* @return {SearchSource}
*/
extend() {
return new SearchSource().inherits(this);
}
/**
* return a simple, encodable object representing the state of the SearchSource
* @return {[type]} [description]
*/
toJSON = function () {
return _.clone(this._state);
};
/**
* Create a string representation of the object
* @return {[type]} [description]
*/
toString() {
return angular.toJson(this.toJSON());
}
/**
* Put a request in to the courier that this Source should
* be fetched on the next run of the courier
* @return {Promise}
*/
onResults() {
const self = this;
return new Promise(function (resolve, reject) {
const defer = Promise.defer();
defer.promise.then(resolve, reject);
const errorHandler = (request, error) => {
reject(error);
request.abort();
};
self._createRequest({ defer, errorHandler });
});
}
/**
* Fetch this source and reject the returned Promise on error
*
@ -417,7 +326,7 @@ export function SearchSourceProvider(Promise, Private, config) {
}
/**
* Cancel all pending requests for this dataSource
* Cancel all pending requests for this searchSource
* @return {undefined}
*/
cancelQueued() {
@ -426,15 +335,6 @@ export function SearchSourceProvider(Promise, Private, config) {
.forEach(req => req.abort());
}
/**
* Completely destroy the SearchSource.
* @return {undefined}
*/
destroy() {
this.cancelQueued();
this._requestStartHandlers.length = 0;
}
/**
* Add a handler that will be notified whenever requests start
* @param {Function} handler
@ -469,6 +369,68 @@ export function SearchSourceProvider(Promise, Private, config) {
.then(_.noop);
}
/**
* Put a request in to the courier that this Source should
* be fetched on the next run of the courier
* @return {Promise}
*/
onResults() {
const self = this;
return new Promise(function (resolve, reject) {
const defer = Promise.defer();
defer.promise.then(resolve, reject);
const errorHandler = (request, error) => {
reject(error);
request.abort();
};
self._createRequest({ defer, errorHandler });
});
}
onBeginSegmentedFetch(initFunction) {
const self = this;
return new Promise((resolve, reject) => {
function addRequest() {
const defer = Promise.defer();
const errorHandler = (request, error) => {
reject(error);
request.abort();
};
const req = new SegmentedSearchRequest({ source: self, defer, errorHandler, initFn: initFunction });
// Return promises created by the completion handler so that
// errors will bubble properly
return req.getCompletePromise().then(addRequest);
}
addRequest();
});
}
async getSearchRequestBody() {
const searchRequest = await this._flatten();
return searchRequest.body;
}
/**
* Called by requests of this search source when they are done
* @param {Courier.Request} request
* @return {undefined}
*/
requestIsStopped() {
this.activeFetchCount -= 1;
}
/**
* Completely destroy the SearchSource.
* @return {undefined}
*/
destroy() {
this.cancelQueued();
this._requestStartHandlers.length = 0;
}
/******
* PRIVATE APIS
@ -480,14 +442,6 @@ export function SearchSourceProvider(Promise, Private, config) {
.filter(req => req.source === this);
}
/**
* Gets the type of the DataSource
* @return {string}
*/
_getType() {
return 'search';
}
/**
* Create a common search request object, which should
* be put into the pending request queue, for this search
@ -502,20 +456,20 @@ export function SearchSourceProvider(Promise, Private, config) {
}
/**
* Used to merge properties into the state within ._flatten().
* The state is passed in and modified by the function
* Used to merge properties into the data within ._flatten().
* The data is passed in and modified by the function
*
* @param {object} state - the current merged state
* @param {object} data - the current merged data
* @param {*} val - the value at `key`
* @param {*} key - The key of `val`
* @return {undefined}
*/
_mergeProp(state, val, key) {
_mergeProp(data, val, key) {
if (typeof val === 'function') {
const source = this;
return Promise.cast(val(this))
.then(function (newVal) {
return source._mergeProp(state, newVal, key);
return source._mergeProp(data, newVal, key);
});
}
@ -526,17 +480,17 @@ export function SearchSourceProvider(Promise, Private, config) {
let filters = Array.isArray(val) ? val : [val];
filters = filters.filter(filter => {
return this._filterPredicates.every(predicate => predicate(filter, state));
return this._filterPredicates.every(predicate => predicate(filter, data));
});
state.filters = [...(state.filters || []), ...filters];
data.filters = [...(data.filters || []), ...filters];
return;
case 'index':
case 'type':
case 'id':
case 'highlightAll':
if (key && state[key] == null) {
state[key] = val;
if (key && data[key] == null) {
data[key] = val;
}
return;
case 'searchAfter':
@ -548,14 +502,14 @@ export function SearchSourceProvider(Promise, Private, config) {
addToBody();
break;
case 'sort':
val = normalizeSortRequest(val, this.get('index'));
val = normalizeSortRequest(val, this.getField('index'));
addToBody();
break;
case 'query':
state.query = (state.query || []).concat(val);
data.query = (data.query || []).concat(val);
break;
case 'fields':
state[key] = _.uniq([...(state[key] || []), ...val]);
data[key] = _.uniq([...(data[key] || []), ...val]);
break;
default:
addToBody();
@ -565,10 +519,10 @@ export function SearchSourceProvider(Promise, Private, config) {
* Add the key and val to the body of the request
*/
function addToBody() {
state.body = state.body || {};
data.body = data.body || {};
// ignore if we already have a value
if (state.body[key] == null) {
state.body[key] = val;
if (data.body[key] == null) {
data.body[key] = val;
}
}
}
@ -577,15 +531,13 @@ export function SearchSourceProvider(Promise, Private, config) {
* Walk the inheritance chain of a source and return it's
* flat representation (taking into account merging rules)
* @returns {Promise}
* @resolved {Object|null} - the flat state of the SearchSource
* @resolved {Object|null} - the flat data of the SearchSource
*/
_flatten() {
const type = this._getType();
// the merged data of this dataSource and it's ancestors
const flatData = {};
// the merged state of this dataSource and it's ancestors
const flatState = {};
// function used to write each property from each state object in the chain to flat state
// function used to write each property from each data object in the chain to flat data
const root = this;
// start the chain at this source
@ -593,17 +545,17 @@ export function SearchSourceProvider(Promise, Private, config) {
// call the ittr and return it's promise
return (function ittr() {
// iterate the _state object (not array) and
// iterate the _fields object (not array) and
// pass each key:value pair to source._mergeProp. if _mergeProp
// returns a promise, then wait for it to complete and call _mergeProp again
return Promise.all(_.map(current._state, function ittr(value, key) {
return Promise.all(_.map(current._fields, function ittr(value, key) {
if (Promise.is(value)) {
return value.then(function (value) {
return ittr(value, key);
});
}
const prom = root._mergeProp(flatState, value, key);
const prom = root._mergeProp(flatData, value, key);
return Promise.is(prom) ? prom : null;
}))
.then(function () {
@ -617,82 +569,80 @@ export function SearchSourceProvider(Promise, Private, config) {
});
}())
.then(function () {
if (type === 'search') {
// This is down here to prevent the circular dependency
flatState.body = flatState.body || {};
// This is down here to prevent the circular dependency
flatData.body = flatData.body || {};
const computedFields = flatState.index.getComputedFields();
flatState.body.stored_fields = computedFields.storedFields;
flatState.body.script_fields = flatState.body.script_fields || {};
flatState.body.docvalue_fields = flatState.body.docvalue_fields || [];
const computedFields = flatData.index.getComputedFields();
flatData.body.stored_fields = computedFields.storedFields;
flatData.body.script_fields = flatData.body.script_fields || {};
flatData.body.docvalue_fields = flatData.body.docvalue_fields || [];
_.extend(flatState.body.script_fields, computedFields.scriptFields);
flatState.body.docvalue_fields = _.union(flatState.body.docvalue_fields, computedFields.docvalueFields);
_.extend(flatData.body.script_fields, computedFields.scriptFields);
flatData.body.docvalue_fields = _.union(flatData.body.docvalue_fields, computedFields.docvalueFields);
if (flatState.body._source) {
// exclude source fields for this index pattern specified by the user
const filter = fieldWildcardFilter(flatState.body._source.excludes);
flatState.body.docvalue_fields = flatState.body.docvalue_fields.filter(filter);
}
// if we only want to search for certain fields
const fields = flatState.fields;
if (fields) {
// filter out the docvalue_fields, and script_fields to only include those that we are concerned with
flatState.body.docvalue_fields = _.intersection(flatState.body.docvalue_fields, fields);
flatState.body.script_fields = _.pick(flatState.body.script_fields, fields);
// request the remaining fields from both stored_fields and _source
const remainingFields = _.difference(fields, _.keys(flatState.body.script_fields));
flatState.body.stored_fields = remainingFields;
_.set(flatState.body, '_source.includes', remainingFields);
}
flatState.body.query = buildESQuery(flatState.index, flatState.query, flatState.filters);
if (flatState.highlightAll != null) {
if (flatState.highlightAll && flatState.body.query) {
flatState.body.highlight = getHighlightRequest(flatState.body.query, getConfig);
}
delete flatState.highlightAll;
}
/**
* Translate a filter into a query to support es 3+
* @param {Object} filter - The filter to translate
* @return {Object} the query version of that filter
*/
const translateToQuery = function (filter) {
if (!filter) return;
if (filter.query) {
return filter.query;
}
return filter;
};
// re-write filters within filter aggregations
(function recurse(aggBranch) {
if (!aggBranch) return;
Object.keys(aggBranch).forEach(function (id) {
const agg = aggBranch[id];
if (agg.filters) {
// translate filters aggregations
const filters = agg.filters.filters;
Object.keys(filters).forEach(function (filterId) {
filters[filterId] = translateToQuery(filters[filterId]);
});
}
recurse(agg.aggs || agg.aggregations);
});
}(flatState.body.aggs || flatState.body.aggregations));
if (flatData.body._source) {
// exclude source fields for this index pattern specified by the user
const filter = fieldWildcardFilter(flatData.body._source.excludes);
flatData.body.docvalue_fields = flatData.body.docvalue_fields.filter(filter);
}
return flatState;
// if we only want to search for certain fields
const fields = flatData.fields;
if (fields) {
// filter out the docvalue_fields, and script_fields to only include those that we are concerned with
flatData.body.docvalue_fields = _.intersection(flatData.body.docvalue_fields, fields);
flatData.body.script_fields = _.pick(flatData.body.script_fields, fields);
// request the remaining fields from both stored_fields and _source
const remainingFields = _.difference(fields, _.keys(flatData.body.script_fields));
flatData.body.stored_fields = remainingFields;
_.set(flatData.body, '_source.includes', remainingFields);
}
flatData.body.query = buildESQuery(flatData.index, flatData.query, flatData.filters);
if (flatData.highlightAll != null) {
if (flatData.highlightAll && flatData.body.query) {
flatData.body.highlight = getHighlightRequest(flatData.body.query, getConfig);
}
delete flatData.highlightAll;
}
/**
* Translate a filter into a query to support es 3+
* @param {Object} filter - The filter to translate
* @return {Object} the query version of that filter
*/
const translateToQuery = function (filter) {
if (!filter) return;
if (filter.query) {
return filter.query;
}
return filter;
};
// re-write filters within filter aggregations
(function recurse(aggBranch) {
if (!aggBranch) return;
Object.keys(aggBranch).forEach(function (id) {
const agg = aggBranch[id];
if (agg.filters) {
// translate filters aggregations
const filters = agg.filters.filters;
Object.keys(filters).forEach(function (filterId) {
filters[filterId] = translateToQuery(filters[filterId]);
});
}
recurse(agg.aggs || agg.aggregations);
});
}(flatData.body.aggs || flatData.body.aggregations));
return flatData;
});
}
}

View file

@ -25,7 +25,7 @@
*/
function getRequestInspectorStats(searchSource) {
const stats = {};
const index = searchSource.get('index');
const index = searchSource.getField('index');
if (index) {
stats['Index pattern'] = {

View file

@ -72,7 +72,7 @@ describe('docTable', function () {
searchSource = Private(FixturesStubbedSearchSourceProvider);
});
init($elem, {
searchSource: searchSource,
searchSource,
columns: [],
sorting: ['@timestamp', 'desc']
});
@ -89,12 +89,12 @@ describe('docTable', function () {
});
it('should set the indexPattern to that of the searchSource', function () {
expect($scope.indexPattern).to.be(searchSource.get('index'));
expect($scope.indexPattern).to.be(searchSource.getField('index'));
});
it('should set size and sort on the searchSource', function () {
expect($scope.searchSource.sort.called).to.be(true);
expect($scope.searchSource.size.called).to.be(true);
expect($scope.searchSource.setField.getCall(0).args[0]).to.be('size');
expect($scope.searchSource.setField.getCall(1).args[0]).to.be('sort');
});
it('should have an addRows function that increases the row count', function () {

View file

@ -94,16 +94,16 @@ uiModules.get('kibana')
$scope.$watch('searchSource', function () {
if (!$scope.searchSource) return;
$scope.indexPattern = $scope.searchSource.get('index');
$scope.indexPattern = $scope.searchSource.getField('index');
$scope.searchSource.size(config.get('discover:sampleSize'));
$scope.searchSource.sort(getSort($scope.sorting, $scope.indexPattern));
$scope.searchSource.setField('size', config.get('discover:sampleSize'));
$scope.searchSource.setField('sort', getSort($scope.sorting, $scope.indexPattern));
// Set the watcher after initialization
$scope.$watchCollection('sorting', function (newSort, oldSort) {
// Don't react if sort values didn't really change
if (newSort === oldSort) return;
$scope.searchSource.sort(getSort(newSort, $scope.indexPattern));
$scope.searchSource.setField('sort', getSort(newSort, $scope.indexPattern));
$scope.searchSource.fetchQueued();
});

View file

@ -82,8 +82,8 @@ const CourierRequestHandlerProvider = function () {
// Using callParentStartHandlers: true we make sure, that the parent searchSource
// onSearchRequestStart will be called properly even though we use an inherited
// search source.
const timeFilterSearchSource = searchSource.makeChild({ callParentStartHandlers: true });
const requestSearchSource = timeFilterSearchSource.makeChild({ callParentStartHandlers: true });
const timeFilterSearchSource = searchSource.createChild({ callParentStartHandlers: true });
const requestSearchSource = timeFilterSearchSource.createChild({ callParentStartHandlers: true });
// For now we need to mirror the history of the passed search source, since
// the spy panel wouldn't work otherwise.
@ -96,7 +96,7 @@ const CourierRequestHandlerProvider = function () {
}
});
requestSearchSource.aggs(function () {
requestSearchSource.setField('aggs', function () {
return aggs.toDsl();
});
@ -104,12 +104,12 @@ const CourierRequestHandlerProvider = function () {
return aggs.onSearchRequestStart(searchSource, searchRequest);
});
timeFilterSearchSource.set('filter', () => {
return getTime(searchSource.get('index'), timeRange);
timeFilterSearchSource.setField('filter', () => {
return getTime(searchSource.getField('index'), timeRange);
});
requestSearchSource.set('filter', filters);
requestSearchSource.set('query', query);
requestSearchSource.setField('filter', filters);
requestSearchSource.setField('query', query);
const shouldQuery = (requestBodyHash) => {
if (!searchSource.lastQuery || forceFetch) return true;

View file

@ -64,8 +64,8 @@ describe('visualize directive', function () {
$rootScope.uiState = uiState;
$rootScope.searchSource = searchSource;
$rootScope.savedObject = {
vis: vis,
searchSource: searchSource
vis,
searchSource,
};
$rootScope.updateState = updateState;

View file

@ -42,8 +42,8 @@ describe('visualize loader', () => {
function createSavedObject() {
return {
vis: vis,
searchSource: searchSource
vis,
searchSource,
};
}

View file

@ -185,9 +185,9 @@ module.directive('mlCustomUrlEditor', function (Private) {
const searchSourceJSON = response.get('kibanaSavedObjectMeta.searchSourceJSON');
if (searchSourceJSON !== undefined) {
const searchSource = JSON.parse(searchSourceJSON);
filters = _.get(searchSource, 'filter', []);
query = searchSource.query;
const searchSourceData = JSON.parse(searchSourceJSON);
filters = _.get(searchSourceData, 'filter', []);
query = searchSourceData.query;
}
// Add time settings to the global state URL parameter with $earliest$ and

View file

@ -56,12 +56,12 @@ export function createSearchItems($route) {
if (indexPattern.id === undefined &&
savedSearch.id !== undefined) {
indexPattern = searchSource.get('index');
indexPattern = searchSource.getField('index');
// Extract the query from the searchSource
// Might be as a String in q.query, or
// nested inside q.query.query_string
const q = searchSource.get('query');
const q = searchSource.getField('query');
if (q !== undefined && q.language === 'lucene' && q.query !== undefined) {
if (typeof q.query === 'string' && q.query !== '') {
query.query_string.query = q.query;
@ -71,7 +71,7 @@ export function createSearchItems($route) {
}
}
const fs = searchSource.get('filter');
const fs = searchSource.getField('filter');
if (fs.length) {
filters = fs;
}