Kuery/KQL: Support not passing an index pattern (#28010)

* Support not sending index pattern to Kuery functions

* fix match all inside is
This commit is contained in:
Lukas Olson 2019-01-07 02:44:34 -07:00 committed by Marco Vettorello
parent 9311f19732
commit 5de724f772
11 changed files with 80 additions and 7 deletions

View file

@ -52,6 +52,8 @@ function fromExpression(expression, parseOptions = {}, parse = parseKuery) {
return parse(expression, parseOptions);
}
// indexPattern isn't required, but if you pass one in, we can be more intelligent
// about how we craft the queries (e.g. scripted fields)
export function toElasticsearchQuery(node, indexPattern) {
if (!node || !node.type || !nodeTypes[node.type]) {
return toElasticsearchQuery(nodeTypes.function.buildNode('and', []));

View file

@ -57,12 +57,21 @@ describe('kuery functions', function () {
expect(_.isEqual(expected, result)).to.be(true);
});
it('should return an ES exists query without an index pattern', function () {
const expected = {
exists: { field: 'response' }
};
const existsNode = nodeTypes.function.buildNode('exists', 'response');
const result = exists.toElasticsearchQuery(existsNode);
expect(_.isEqual(expected, result)).to.be(true);
});
it('should throw an error for scripted fields', function () {
const existsNode = nodeTypes.function.buildNode('exists', 'script string');
expect(exists.toElasticsearchQuery)
.withArgs(existsNode, indexPattern).to.throwException(/Exists query does not support scripted fields/);
});
});
});
});

View file

@ -83,6 +83,14 @@ describe('kuery functions', function () {
expect(result.geo_bounding_box.geo).to.have.property('bottom_right', '50.73, -135.35');
});
it('should return an ES geo_bounding_box query without an index pattern', function () {
const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params);
const result = geoBoundingBox.toElasticsearchQuery(node);
expect(result).to.have.property('geo_bounding_box');
expect(result.geo_bounding_box.geo).to.have.property('top_left', '73.12, -174.37');
expect(result.geo_bounding_box.geo).to.have.property('bottom_right', '50.73, -135.35');
});
it('should use the ignore_unmapped parameter', function () {
const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params);
const result = geoBoundingBox.toElasticsearchQuery(node, indexPattern);

View file

@ -91,6 +91,17 @@ describe('kuery functions', function () {
});
});
it('should return an ES geo_polygon query without an index pattern', function () {
const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points);
const result = geoPolygon.toElasticsearchQuery(node);
expect(result).to.have.property('geo_polygon');
expect(result.geo_polygon.geo).to.have.property('points');
result.geo_polygon.geo.points.forEach((point, index) => {
const expectedLatLon = `${points[index].lat}, ${points[index].lon}`;
expect(point).to.be(expectedLatLon);
});
});
it('should use the ignore_unmapped parameter', function () {
const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points);

View file

@ -143,6 +143,21 @@ describe('kuery functions', function () {
expect(result).to.eql(expected);
});
it('should return an ES match query when a concrete fieldName and value are provided without an index pattern', function () {
const expected = {
bool: {
should: [
{ match: { extension: 'jpg' } },
],
minimum_should_match: 1
}
};
const node = nodeTypes.function.buildNode('is', 'extension', 'jpg');
const result = is.toElasticsearchQuery(node);
expect(result).to.eql(expected);
});
it('should support creation of phrase queries', function () {
const expected = {
bool: {

View file

@ -86,6 +86,28 @@ describe('kuery functions', function () {
expect(result).to.eql(expected);
});
it('should return an ES range query without an index pattern', function () {
const expected = {
bool: {
should: [
{
range: {
bytes: {
gt: 1000,
lt: 8000
}
}
}
],
minimum_should_match: 1
}
};
const node = nodeTypes.function.buildNode('range', 'bytes', { gt: 1000, lt: 8000 });
const result = range.toElasticsearchQuery(node);
expect(result).to.eql(expected);
});
it('should support wildcard field names', function () {
const expected = {
bool: {

View file

@ -17,6 +17,7 @@
* under the License.
*/
import { get } from 'lodash';
import * as literal from '../node_types/literal';
export function buildNodeParams(fieldName) {
@ -28,7 +29,7 @@ export function buildNodeParams(fieldName) {
export function toElasticsearchQuery(node, indexPattern) {
const { arguments: [ fieldNameArg ] } = node;
const fieldName = literal.toElasticsearchQuery(fieldNameArg);
const field = indexPattern.fields.find(field => field.name === fieldName);
const field = get(indexPattern, 'fields', []).find(field => field.name === fieldName);
if (field && field.scripted) {
throw new Error(`Exists query does not support scripted fields`);

View file

@ -37,7 +37,7 @@ export function buildNodeParams(fieldName, params) {
export function toElasticsearchQuery(node, indexPattern) {
const [ fieldNameArg, ...args ] = node.arguments;
const fieldName = nodeTypes.literal.toElasticsearchQuery(fieldNameArg);
const field = indexPattern.fields.find(field => field.name === fieldName);
const field = _.get(indexPattern, 'fields', []).find(field => field.name === fieldName);
const queryParams = args.reduce((acc, arg) => {
const snakeArgName = _.snakeCase(arg.name);
return {

View file

@ -17,6 +17,7 @@
* under the License.
*/
import { get } from 'lodash';
import { nodeTypes } from '../node_types';
import * as ast from '../ast';
@ -35,7 +36,7 @@ export function buildNodeParams(fieldName, points) {
export function toElasticsearchQuery(node, indexPattern) {
const [ fieldNameArg, ...points ] = node.arguments;
const fieldName = nodeTypes.literal.toElasticsearchQuery(fieldNameArg);
const field = indexPattern.fields.find(field => field.name === fieldName);
const field = get(indexPattern, 'fields', []).find(field => field.name === fieldName);
const queryParams = {
points: points.map(ast.toElasticsearchQuery)
};

View file

@ -44,6 +44,7 @@ export function buildNodeParams(fieldName, value, isPhrase = false) {
export function toElasticsearchQuery(node, indexPattern) {
const { arguments: [ fieldNameArg, valueArg, isPhraseArg ] } = node;
const fieldName = ast.toElasticsearchQuery(fieldNameArg);
const value = !_.isUndefined(valueArg) ? ast.toElasticsearchQuery(valueArg) : valueArg;
const type = isPhraseArg.value ? 'phrase' : 'best_fields';
@ -65,7 +66,7 @@ export function toElasticsearchQuery(node, indexPattern) {
};
}
const fields = getFields(fieldNameArg, indexPattern);
const fields = indexPattern ? getFields(fieldNameArg, indexPattern) : [];
// If no fields are found in the index pattern we send through the given field name as-is. We do this to preserve
// the behaviour of lucene on dashboards where there are panels based on different index patterns that have different
@ -80,7 +81,10 @@ export function toElasticsearchQuery(node, indexPattern) {
}
const isExistsQuery = valueArg.type === 'wildcard' && value === '*';
const isMatchAllQuery = isExistsQuery && fields && fields.length === indexPattern.fields.length;
const isAllFieldsQuery =
(fieldNameArg.type === 'wildcard' && fieldName === '*')
|| (fields && indexPattern && fields.length === indexPattern.fields.length);
const isMatchAllQuery = isExistsQuery && isAllFieldsQuery;
if (isMatchAllQuery) {
return { match_all: {} };

View file

@ -37,7 +37,7 @@ export function buildNodeParams(fieldName, params) {
export function toElasticsearchQuery(node, indexPattern) {
const [ fieldNameArg, ...args ] = node.arguments;
const fields = getFields(fieldNameArg, indexPattern);
const fields = indexPattern ? getFields(fieldNameArg, indexPattern) : [];
const namedArgs = extractArguments(args);
const queryParams = _.mapValues(namedArgs, ast.toElasticsearchQuery);