mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
parent
87ad98d8a0
commit
a812ee93fe
116 changed files with 2030 additions and 2873 deletions
|
@ -8,6 +8,7 @@ bower_components
|
|||
/optimize
|
||||
/built_assets
|
||||
/html_docs
|
||||
/src/plugins/data/common/es_query/kuery/ast/_generated_/**
|
||||
/src/fixtures/vislib/mock_data
|
||||
/src/legacy/ui/public/angular-bootstrap
|
||||
/src/legacy/ui/public/flot-charts
|
||||
|
@ -19,7 +20,6 @@ bower_components
|
|||
/src/core/lib/kbn_internal_native_observable
|
||||
/packages/*/target
|
||||
/packages/eslint-config-kibana
|
||||
/packages/kbn-es-query/src/kuery/ast/kuery.js
|
||||
/packages/kbn-pm/dist
|
||||
/packages/kbn-plugin-generator/sao_template/template
|
||||
/packages/kbn-ui-framework/dist
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"interpreter": "src/legacy/core_plugins/interpreter",
|
||||
"kbn": "src/legacy/core_plugins/kibana",
|
||||
"kbnDocViews": "src/legacy/core_plugins/kbn_doc_views",
|
||||
"kbnESQuery": "packages/kbn-es-query",
|
||||
"kbnVislibVisTypes": "src/legacy/core_plugins/kbn_vislib_vis_types",
|
||||
"kibana_react": "src/legacy/core_plugins/kibana_react",
|
||||
"kibana-react": "src/plugins/kibana_react",
|
||||
|
|
|
@ -9,5 +9,5 @@ Search for objects
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "filter" | "type" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "page" | "perPage" | "fields">) => Promise<SavedObjectsFindResponsePublic<T>>;
|
||||
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "filter" | "type" | "fields" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "page" | "perPage">) => Promise<SavedObjectsFindResponsePublic<T>>;
|
||||
```
|
||||
|
|
|
@ -20,7 +20,7 @@ export declare class SavedObjectsClient
|
|||
| [bulkGet](./kibana-plugin-public.savedobjectsclient.bulkget.md) | | <code>(objects?: {</code><br/><code> id: string;</code><br/><code> type: string;</code><br/><code> }[]) => Promise<SavedObjectsBatchResponse<SavedObjectAttributes>></code> | Returns an array of objects by id |
|
||||
| [create](./kibana-plugin-public.savedobjectsclient.create.md) | | <code><T extends SavedObjectAttributes>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>></code> | Persists an object |
|
||||
| [delete](./kibana-plugin-public.savedobjectsclient.delete.md) | | <code>(type: string, id: string) => Promise<{}></code> | Deletes an object |
|
||||
| [find](./kibana-plugin-public.savedobjectsclient.find.md) | | <code><T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "filter" | "type" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "page" | "perPage" | "fields">) => Promise<SavedObjectsFindResponsePublic<T>></code> | Search for objects |
|
||||
| [find](./kibana-plugin-public.savedobjectsclient.find.md) | | <code><T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "filter" | "type" | "fields" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "page" | "perPage">) => Promise<SavedObjectsFindResponsePublic<T>></code> | Search for objects |
|
||||
| [get](./kibana-plugin-public.savedobjectsclient.get.md) | | <code><T extends SavedObjectAttributes>(type: string, id: string) => Promise<SimpleSavedObject<T>></code> | Fetches a single object |
|
||||
|
||||
## Methods
|
||||
|
|
|
@ -119,7 +119,6 @@
|
|||
"@kbn/babel-code-parser": "1.0.0",
|
||||
"@kbn/babel-preset": "1.0.0",
|
||||
"@kbn/config-schema": "1.0.0",
|
||||
"@kbn/es-query": "1.0.0",
|
||||
"@kbn/i18n": "1.0.0",
|
||||
"@kbn/interpreter": "1.0.0",
|
||||
"@kbn/pm": "1.0.0",
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
# kbn-es-query
|
||||
|
||||
This module is responsible for generating Elasticsearch queries for Kibana. See explanations below for each of the subdirectories.
|
||||
|
||||
## es_query
|
||||
|
||||
This folder contains the code that combines Lucene/KQL queries and filters into an Elasticsearch query.
|
||||
|
||||
```javascript
|
||||
buildEsQuery(indexPattern, queries, filters, config)
|
||||
```
|
||||
|
||||
Generates the Elasticsearch query DSL from combining the queries and filters provided.
|
||||
|
||||
```javascript
|
||||
buildQueryFromFilters(filters, indexPattern)
|
||||
```
|
||||
|
||||
Generates the Elasticsearch query DSL from the given filters.
|
||||
|
||||
```javascript
|
||||
luceneStringToDsl(query)
|
||||
```
|
||||
|
||||
Generates the Elasticsearch query DSL from the given Lucene query.
|
||||
|
||||
```javascript
|
||||
migrateFilter(filter, indexPattern)
|
||||
```
|
||||
|
||||
Migrates a filter from a previous version of Elasticsearch to the current version.
|
||||
|
||||
```javascript
|
||||
decorateQuery(query, queryStringOptions)
|
||||
```
|
||||
|
||||
Decorates an Elasticsearch query_string query with the given options.
|
||||
|
||||
## filters
|
||||
|
||||
This folder contains the code related to Kibana Filter objects, including their definitions, and helper functions to create them. Filters in Kibana always contain a `meta` property which describes which `index` the filter corresponds to, as well as additional data about the specific filter.
|
||||
|
||||
The object that is created by each of the following functions corresponds to a Filter object in the `lib` directory (e.g. `PhraseFilter`, `RangeFilter`, etc.)
|
||||
|
||||
```javascript
|
||||
buildExistsFilter(field, indexPattern)
|
||||
```
|
||||
|
||||
Creates a filter (`ExistsFilter`) where the given field exists.
|
||||
|
||||
```javascript
|
||||
buildPhraseFilter(field, value, indexPattern)
|
||||
```
|
||||
|
||||
Creates an filter (`PhraseFilter`) where the given field matches the given value.
|
||||
|
||||
```javascript
|
||||
buildPhrasesFilter(field, params, indexPattern)
|
||||
```
|
||||
|
||||
Creates a filter (`PhrasesFilter`) where the given field matches one or more of the given values. `params` should be an array of values.
|
||||
|
||||
```javascript
|
||||
buildQueryFilter(query, index)
|
||||
```
|
||||
|
||||
Creates a filter (`CustomFilter`) corresponding to a raw Elasticsearch query DSL object.
|
||||
|
||||
```javascript
|
||||
buildRangeFilter(field, params, indexPattern)
|
||||
```
|
||||
|
||||
Creates a filter (`RangeFilter`) where the value for the given field is in the given range. `params` should contain `lt`, `lte`, `gt`, and/or `gte`.
|
||||
|
||||
## kuery
|
||||
|
||||
This folder contains the code corresponding to generating Elasticsearch queries using the Kibana query language.
|
||||
|
||||
In general, you will only need to worry about the following functions from the `ast` folder:
|
||||
|
||||
```javascript
|
||||
fromExpression(expression)
|
||||
```
|
||||
|
||||
Generates an abstract syntax tree corresponding to the raw Kibana query `expression`.
|
||||
|
||||
```javascript
|
||||
toElasticsearchQuery(node, indexPattern)
|
||||
```
|
||||
|
||||
Takes an abstract syntax tree (generated from the previous method) and generates the Elasticsearch query DSL using the given `indexPattern`. Note that if no `indexPattern` is provided, then an Elasticsearch query DSL will still be generated, ignoring things like the index pattern scripted fields, field types, etc.
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
// We can't use common Kibana presets here because of babel versions incompatibility
|
||||
module.exports = {
|
||||
env: {
|
||||
public: {
|
||||
presets: [
|
||||
'@kbn/babel-preset/webpack_preset'
|
||||
],
|
||||
},
|
||||
server: {
|
||||
presets: [
|
||||
'@kbn/babel-preset/node_preset'
|
||||
],
|
||||
},
|
||||
},
|
||||
ignore: ['**/__tests__/**/*', '**/*.test.ts', '**/*.test.tsx'],
|
||||
};
|
20
packages/kbn-es-query/index.d.ts
vendored
20
packages/kbn-es-query/index.d.ts
vendored
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export * from './src';
|
|
@ -1,29 +0,0 @@
|
|||
{
|
||||
"name": "@kbn/es-query",
|
||||
"main": "target/server/index.js",
|
||||
"browser": "target/public/index.js",
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "node scripts/build",
|
||||
"kbn:bootstrap": "node scripts/build --source-maps",
|
||||
"kbn:watch": "node scripts/build --source-maps --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": "npm:@elastic/lodash@3.10.1-kibana3",
|
||||
"moment-timezone": "^0.5.27",
|
||||
"@kbn/i18n": "1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.5.5",
|
||||
"@babel/core": "^7.5.5",
|
||||
"@kbn/babel-preset": "1.0.0",
|
||||
"@kbn/dev-utils": "1.0.0",
|
||||
"@kbn/expect": "1.0.0",
|
||||
"del": "^5.1.0",
|
||||
"getopts": "^2.2.4",
|
||||
"supports-color": "^7.0.0",
|
||||
"typescript": "3.5.3"
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
require('../tasks/build_cli');
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"meta": {
|
||||
"index": "logstash-*"
|
||||
}
|
||||
}
|
|
@ -1,303 +0,0 @@
|
|||
{
|
||||
"id": "logstash-*",
|
||||
"title": "logstash-*",
|
||||
"fields": [
|
||||
{
|
||||
"name": "bytes",
|
||||
"type": "number",
|
||||
"esTypes": ["long"],
|
||||
"count": 10,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "ssl",
|
||||
"type": "boolean",
|
||||
"esTypes": ["boolean"],
|
||||
"count": 20,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "@timestamp",
|
||||
"type": "date",
|
||||
"esTypes": ["date"],
|
||||
"count": 30,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "time",
|
||||
"type": "date",
|
||||
"esTypes": ["date"],
|
||||
"count": 30,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "@tags",
|
||||
"type": "string",
|
||||
"esTypes": ["keyword"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "utc_time",
|
||||
"type": "date",
|
||||
"esTypes": ["date"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "phpmemory",
|
||||
"type": "number",
|
||||
"esTypes": ["integer"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "ip",
|
||||
"type": "ip",
|
||||
"esTypes": ["ip"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "request_body",
|
||||
"type": "attachment",
|
||||
"esTypes": ["attachment"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "point",
|
||||
"type": "geo_point",
|
||||
"esTypes": ["geo_point"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "area",
|
||||
"type": "geo_shape",
|
||||
"esTypes": ["geo_shape"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "hashed",
|
||||
"type": "murmur3",
|
||||
"esTypes": ["murmur3"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": false,
|
||||
"readFromDocValues": false
|
||||
},
|
||||
{
|
||||
"name": "geo.coordinates",
|
||||
"type": "geo_point",
|
||||
"esTypes": ["geo_point"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "extension",
|
||||
"type": "string",
|
||||
"esTypes": ["keyword"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "machine.os",
|
||||
"type": "string",
|
||||
"esTypes": ["text"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": false
|
||||
},
|
||||
{
|
||||
"name": "machine.os.raw",
|
||||
"type": "string",
|
||||
"esTypes": ["keyword"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true,
|
||||
"subType": { "multi": { "parent": "machine.os" } }
|
||||
},
|
||||
{
|
||||
"name": "geo.src",
|
||||
"type": "string",
|
||||
"esTypes": ["keyword"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "string",
|
||||
"esTypes": ["_id"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": false
|
||||
},
|
||||
{
|
||||
"name": "_type",
|
||||
"type": "string",
|
||||
"esTypes": ["_type"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": false
|
||||
},
|
||||
{
|
||||
"name": "_source",
|
||||
"type": "_source",
|
||||
"esTypes": ["_source"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": false
|
||||
},
|
||||
{
|
||||
"name": "non-filterable",
|
||||
"type": "string",
|
||||
"esTypes": ["text"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": false,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": false
|
||||
},
|
||||
{
|
||||
"name": "non-sortable",
|
||||
"type": "string",
|
||||
"esTypes": ["text"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": false,
|
||||
"aggregatable": false,
|
||||
"readFromDocValues": false
|
||||
},
|
||||
{
|
||||
"name": "custom_user_field",
|
||||
"type": "conflict",
|
||||
"esTypes": ["long", "text"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": true
|
||||
},
|
||||
{
|
||||
"name": "script string",
|
||||
"type": "string",
|
||||
"count": 0,
|
||||
"scripted": true,
|
||||
"script": "'i am a string'",
|
||||
"lang": "expression",
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": false
|
||||
},
|
||||
{
|
||||
"name": "script number",
|
||||
"type": "number",
|
||||
"count": 0,
|
||||
"scripted": true,
|
||||
"script": "1234",
|
||||
"lang": "expression",
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": false
|
||||
},
|
||||
{
|
||||
"name": "script date",
|
||||
"type": "date",
|
||||
"count": 0,
|
||||
"scripted": true,
|
||||
"script": "1234",
|
||||
"lang": "painless",
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": false
|
||||
},
|
||||
{
|
||||
"name": "script murmur3",
|
||||
"type": "murmur3",
|
||||
"count": 0,
|
||||
"scripted": true,
|
||||
"script": "1234",
|
||||
"lang": "expression",
|
||||
"searchable": true,
|
||||
"aggregatable": true,
|
||||
"readFromDocValues": false
|
||||
},
|
||||
{
|
||||
"name": "nestedField.child",
|
||||
"type": "string",
|
||||
"esTypes": ["text"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": false,
|
||||
"readFromDocValues": false,
|
||||
"subType": { "nested": { "path": "nestedField" } }
|
||||
},
|
||||
{
|
||||
"name": "nestedField.nestedChild.doublyNestedChild",
|
||||
"type": "string",
|
||||
"esTypes": ["text"],
|
||||
"count": 0,
|
||||
"scripted": false,
|
||||
"searchable": true,
|
||||
"aggregatable": false,
|
||||
"readFromDocValues": false,
|
||||
"subType": { "nested": { "path": "nestedField.nestedChild" } }
|
||||
}
|
||||
]
|
||||
}
|
20
packages/kbn-es-query/src/index.d.ts
vendored
20
packages/kbn-es-query/src/index.d.ts
vendored
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export * from './kuery';
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export * from './kuery';
|
|
@ -1,415 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import * as ast from '../ast';
|
||||
import expect from '@kbn/expect';
|
||||
import { nodeTypes } from '../../node_types/index';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
|
||||
let indexPattern;
|
||||
|
||||
describe('kuery AST API', function () {
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
});
|
||||
|
||||
describe('fromKueryExpression', function () {
|
||||
|
||||
it('should return a match all "is" function for whitespace', function () {
|
||||
const expected = nodeTypes.function.buildNode('is', '*', '*');
|
||||
const actual = ast.fromKueryExpression(' ');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should return an "is" function with a null field for single literals', function () {
|
||||
const expected = nodeTypes.function.buildNode('is', null, 'foo');
|
||||
const actual = ast.fromKueryExpression('foo');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should ignore extraneous whitespace at the beginning and end of the query', function () {
|
||||
const expected = nodeTypes.function.buildNode('is', null, 'foo');
|
||||
const actual = ast.fromKueryExpression(' foo ');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should not split on whitespace', function () {
|
||||
const expected = nodeTypes.function.buildNode('is', null, 'foo bar');
|
||||
const actual = ast.fromKueryExpression('foo bar');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support "and" as a binary operator', function () {
|
||||
const expected = nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('is', null, 'foo'),
|
||||
nodeTypes.function.buildNode('is', null, 'bar'),
|
||||
]);
|
||||
const actual = ast.fromKueryExpression('foo and bar');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support "or" as a binary operator', function () {
|
||||
const expected = nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', null, 'foo'),
|
||||
nodeTypes.function.buildNode('is', null, 'bar'),
|
||||
]);
|
||||
const actual = ast.fromKueryExpression('foo or bar');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support negation of queries with a "not" prefix', function () {
|
||||
const expected = nodeTypes.function.buildNode('not',
|
||||
nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', null, 'foo'),
|
||||
nodeTypes.function.buildNode('is', null, 'bar'),
|
||||
])
|
||||
);
|
||||
const actual = ast.fromKueryExpression('not (foo or bar)');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('"and" should have a higher precedence than "or"', function () {
|
||||
const expected = nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', null, 'foo'),
|
||||
nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('is', null, 'bar'),
|
||||
nodeTypes.function.buildNode('is', null, 'baz'),
|
||||
]),
|
||||
nodeTypes.function.buildNode('is', null, 'qux'),
|
||||
])
|
||||
]);
|
||||
const actual = ast.fromKueryExpression('foo or bar and baz or qux');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support grouping to override default precedence', function () {
|
||||
const expected = nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', null, 'foo'),
|
||||
nodeTypes.function.buildNode('is', null, 'bar'),
|
||||
]),
|
||||
nodeTypes.function.buildNode('is', null, 'baz'),
|
||||
]);
|
||||
const actual = ast.fromKueryExpression('(foo or bar) and baz');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support matching against specific fields', function () {
|
||||
const expected = nodeTypes.function.buildNode('is', 'foo', 'bar');
|
||||
const actual = ast.fromKueryExpression('foo:bar');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should also not split on whitespace when matching specific fields', function () {
|
||||
const expected = nodeTypes.function.buildNode('is', 'foo', 'bar baz');
|
||||
const actual = ast.fromKueryExpression('foo:bar baz');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should treat quoted values as phrases', function () {
|
||||
const expected = nodeTypes.function.buildNode('is', 'foo', 'bar baz', true);
|
||||
const actual = ast.fromKueryExpression('foo:"bar baz"');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support a shorthand for matching multiple values against a single field', function () {
|
||||
const expected = nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', 'foo', 'bar'),
|
||||
nodeTypes.function.buildNode('is', 'foo', 'baz'),
|
||||
]);
|
||||
const actual = ast.fromKueryExpression('foo:(bar or baz)');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support "and" and "not" operators and grouping in the shorthand as well', function () {
|
||||
const expected = nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', 'foo', 'bar'),
|
||||
nodeTypes.function.buildNode('is', 'foo', 'baz'),
|
||||
]),
|
||||
nodeTypes.function.buildNode('not',
|
||||
nodeTypes.function.buildNode('is', 'foo', 'qux')
|
||||
),
|
||||
]);
|
||||
const actual = ast.fromKueryExpression('foo:((bar or baz) and not qux)');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support exclusive range operators', function () {
|
||||
const expected = nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('range', 'bytes', {
|
||||
gt: 1000,
|
||||
}),
|
||||
nodeTypes.function.buildNode('range', 'bytes', {
|
||||
lt: 8000,
|
||||
}),
|
||||
]);
|
||||
const actual = ast.fromKueryExpression('bytes > 1000 and bytes < 8000');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support inclusive range operators', function () {
|
||||
const expected = nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('range', 'bytes', {
|
||||
gte: 1000,
|
||||
}),
|
||||
nodeTypes.function.buildNode('range', 'bytes', {
|
||||
lte: 8000,
|
||||
}),
|
||||
]);
|
||||
const actual = ast.fromKueryExpression('bytes >= 1000 and bytes <= 8000');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support wildcards in field names', function () {
|
||||
const expected = nodeTypes.function.buildNode('is', 'machine*', 'osx');
|
||||
const actual = ast.fromKueryExpression('machine*:osx');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support wildcards in values', function () {
|
||||
const expected = nodeTypes.function.buildNode('is', 'foo', 'ba*');
|
||||
const actual = ast.fromKueryExpression('foo:ba*');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should create an exists "is" query when a field is given and "*" is the value', function () {
|
||||
const expected = nodeTypes.function.buildNode('is', 'foo', '*');
|
||||
const actual = ast.fromKueryExpression('foo:*');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support nested queries indicated by curly braces', () => {
|
||||
const expected = nodeTypes.function.buildNode(
|
||||
'nested',
|
||||
'nestedField',
|
||||
nodeTypes.function.buildNode('is', 'childOfNested', 'foo')
|
||||
);
|
||||
const actual = ast.fromKueryExpression('nestedField:{ childOfNested: foo }');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support nested subqueries and subqueries inside nested queries', () => {
|
||||
const expected = nodeTypes.function.buildNode(
|
||||
'and',
|
||||
[
|
||||
nodeTypes.function.buildNode('is', 'response', '200'),
|
||||
nodeTypes.function.buildNode(
|
||||
'nested',
|
||||
'nestedField',
|
||||
nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', 'childOfNested', 'foo'),
|
||||
nodeTypes.function.buildNode('is', 'childOfNested', 'bar'),
|
||||
])
|
||||
)]);
|
||||
const actual = ast.fromKueryExpression('response:200 and nestedField:{ childOfNested:foo or childOfNested:bar }');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support nested sub-queries inside paren groups', () => {
|
||||
const expected = nodeTypes.function.buildNode(
|
||||
'and',
|
||||
[
|
||||
nodeTypes.function.buildNode('is', 'response', '200'),
|
||||
nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode(
|
||||
'nested',
|
||||
'nestedField',
|
||||
nodeTypes.function.buildNode('is', 'childOfNested', 'foo')
|
||||
),
|
||||
nodeTypes.function.buildNode(
|
||||
'nested',
|
||||
'nestedField',
|
||||
nodeTypes.function.buildNode('is', 'childOfNested', 'bar')
|
||||
),
|
||||
])
|
||||
]);
|
||||
const actual = ast.fromKueryExpression('response:200 and ( nestedField:{ childOfNested:foo } or nestedField:{ childOfNested:bar } )');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support nested groups inside other nested groups', () => {
|
||||
const expected = nodeTypes.function.buildNode(
|
||||
'nested',
|
||||
'nestedField',
|
||||
nodeTypes.function.buildNode(
|
||||
'nested',
|
||||
'nestedChild',
|
||||
nodeTypes.function.buildNode('is', 'doublyNestedChild', 'foo')
|
||||
)
|
||||
);
|
||||
const actual = ast.fromKueryExpression('nestedField:{ nestedChild:{ doublyNestedChild:foo } }');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromLiteralExpression', function () {
|
||||
|
||||
it('should create literal nodes for unquoted values with correct primitive types', function () {
|
||||
const stringLiteral = nodeTypes.literal.buildNode('foo');
|
||||
const booleanFalseLiteral = nodeTypes.literal.buildNode(false);
|
||||
const booleanTrueLiteral = nodeTypes.literal.buildNode(true);
|
||||
const numberLiteral = nodeTypes.literal.buildNode(42);
|
||||
|
||||
expect(ast.fromLiteralExpression('foo')).to.eql(stringLiteral);
|
||||
expect(ast.fromLiteralExpression('true')).to.eql(booleanTrueLiteral);
|
||||
expect(ast.fromLiteralExpression('false')).to.eql(booleanFalseLiteral);
|
||||
expect(ast.fromLiteralExpression('42')).to.eql(numberLiteral);
|
||||
});
|
||||
|
||||
it('should allow escaping of special characters with a backslash', function () {
|
||||
const expected = nodeTypes.literal.buildNode('\\():<>"*');
|
||||
// yo dawg
|
||||
const actual = ast.fromLiteralExpression('\\\\\\(\\)\\:\\<\\>\\"\\*');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support double quoted strings that do not need escapes except for quotes', function () {
|
||||
const expected = nodeTypes.literal.buildNode('\\():<>"*');
|
||||
const actual = ast.fromLiteralExpression('"\\():<>\\"*"');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support escaped backslashes inside quoted strings', function () {
|
||||
const expected = nodeTypes.literal.buildNode('\\');
|
||||
const actual = ast.fromLiteralExpression('"\\\\"');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should detect wildcards and build wildcard AST nodes', function () {
|
||||
const expected = nodeTypes.wildcard.buildNode('foo*bar');
|
||||
const actual = ast.fromLiteralExpression('foo*bar');
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
|
||||
it('should return the given node type\'s ES query representation', function () {
|
||||
const node = nodeTypes.function.buildNode('exists', 'response');
|
||||
const expected = nodeTypes.function.toElasticsearchQuery(node, indexPattern);
|
||||
const result = ast.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should return an empty "and" function for undefined nodes and unknown node types', function () {
|
||||
const expected = nodeTypes.function.toElasticsearchQuery(nodeTypes.function.buildNode('and', []));
|
||||
|
||||
expect(ast.toElasticsearchQuery()).to.eql(expected);
|
||||
|
||||
const noTypeNode = nodeTypes.function.buildNode('exists', 'foo');
|
||||
delete noTypeNode.type;
|
||||
expect(ast.toElasticsearchQuery(noTypeNode)).to.eql(expected);
|
||||
|
||||
const unknownTypeNode = nodeTypes.function.buildNode('exists', 'foo');
|
||||
unknownTypeNode.type = 'notValid';
|
||||
expect(ast.toElasticsearchQuery(unknownTypeNode)).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should return the given node type\'s ES query representation including a time zone parameter when one is provided', function () {
|
||||
const config = { dateFormatTZ: 'America/Phoenix' };
|
||||
const node = nodeTypes.function.buildNode('is', '@timestamp', '"2018-04-03T19:04:17"');
|
||||
const expected = nodeTypes.function.toElasticsearchQuery(node, indexPattern, config);
|
||||
const result = ast.toElasticsearchQuery(node, indexPattern, config);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('doesKueryExpressionHaveLuceneSyntaxError', function () {
|
||||
it('should return true for Lucene ranges', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('bar: [1 TO 10]');
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
|
||||
it('should return false for KQL ranges', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('bar < 1');
|
||||
expect(result).to.eql(false);
|
||||
});
|
||||
|
||||
it('should return true for Lucene exists', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('_exists_: bar');
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
|
||||
it('should return false for KQL exists', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('bar:*');
|
||||
expect(result).to.eql(false);
|
||||
});
|
||||
|
||||
it('should return true for Lucene wildcards', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('bar: ba?');
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
|
||||
it('should return false for KQL wildcards', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('bar: ba*');
|
||||
expect(result).to.eql(false);
|
||||
});
|
||||
|
||||
it('should return true for Lucene regex', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('bar: /ba.*/');
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
|
||||
it('should return true for Lucene fuzziness', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('bar: ba~');
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
|
||||
it('should return true for Lucene proximity', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('bar: "ba"~2');
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
|
||||
it('should return true for Lucene boosting', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('bar: ba^2');
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
|
||||
it('should return true for Lucene + operator', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('+foo: bar');
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
|
||||
it('should return true for Lucene - operators', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('-foo: bar');
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
|
||||
it('should return true for Lucene && operators', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('foo: bar && baz: qux');
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
|
||||
it('should return true for Lucene || operators', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('foo: bar || baz: qux');
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
|
||||
it('should return true for mixed KQL/Lucene queries', function () {
|
||||
const result = ast.doesKueryExpressionHaveLuceneSyntaxError('foo: bar and (baz: qux || bag)');
|
||||
expect(result).to.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
50
packages/kbn-es-query/src/kuery/ast/ast.d.ts
vendored
50
packages/kbn-es-query/src/kuery/ast/ast.d.ts
vendored
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { JsonObject } from '..';
|
||||
|
||||
/**
|
||||
* WARNING: these typings are incomplete
|
||||
*/
|
||||
|
||||
export type KueryNode = any;
|
||||
|
||||
export type DslQuery = any;
|
||||
|
||||
export interface KueryParseOptions {
|
||||
helpers: {
|
||||
[key: string]: any;
|
||||
};
|
||||
startRule: string;
|
||||
allowLeadingWildcards: boolean;
|
||||
}
|
||||
|
||||
export function fromKueryExpression(
|
||||
expression: string | DslQuery,
|
||||
parseOptions?: Partial<KueryParseOptions>
|
||||
): KueryNode;
|
||||
|
||||
export function toElasticsearchQuery(
|
||||
node: KueryNode,
|
||||
indexPattern?: any,
|
||||
config?: Record<string, any>,
|
||||
context?: Record<string, any>
|
||||
): JsonObject;
|
||||
|
||||
export function doesKueryExpressionHaveLuceneSyntaxError(expression: string): boolean;
|
20
packages/kbn-es-query/src/kuery/ast/index.d.ts
vendored
20
packages/kbn-es-query/src/kuery/ast/index.d.ts
vendored
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export * from '../ast/ast';
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import * as geoBoundingBox from '../geo_bounding_box';
|
||||
import { nodeTypes } from '../../node_types';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
|
||||
let indexPattern;
|
||||
const params = {
|
||||
bottomRight: {
|
||||
lat: 50.73,
|
||||
lon: -135.35
|
||||
},
|
||||
topLeft: {
|
||||
lat: 73.12,
|
||||
lon: -174.37
|
||||
}
|
||||
};
|
||||
|
||||
describe('kuery functions', function () {
|
||||
describe('geoBoundingBox', function () {
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
});
|
||||
|
||||
describe('buildNodeParams', function () {
|
||||
|
||||
it('should return an "arguments" param', function () {
|
||||
const result = geoBoundingBox.buildNodeParams('geo', params);
|
||||
expect(result).to.only.have.keys('arguments');
|
||||
});
|
||||
|
||||
it('arguments should contain the provided fieldName as a literal', function () {
|
||||
const result = geoBoundingBox.buildNodeParams('geo', params);
|
||||
const { arguments: [ fieldName ] } = result;
|
||||
|
||||
expect(fieldName).to.have.property('type', 'literal');
|
||||
expect(fieldName).to.have.property('value', 'geo');
|
||||
});
|
||||
|
||||
it('arguments should contain the provided params as named arguments with "lat, lon" string values', function () {
|
||||
const result = geoBoundingBox.buildNodeParams('geo', params);
|
||||
const { arguments: [ , ...args ] } = result;
|
||||
|
||||
args.map((param) => {
|
||||
expect(param).to.have.property('type', 'namedArg');
|
||||
expect(['bottomRight', 'topLeft'].includes(param.name)).to.be(true);
|
||||
expect(param.value.type).to.be('literal');
|
||||
|
||||
const expectedParam = params[param.name];
|
||||
const expectedLatLon = `${expectedParam.lat}, ${expectedParam.lon}`;
|
||||
expect(param.value.value).to.be(expectedLatLon);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
|
||||
it('should return an ES geo_bounding_box query representing the given node', function () {
|
||||
const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params);
|
||||
const result = geoBoundingBox.toElasticsearchQuery(node, indexPattern);
|
||||
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 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);
|
||||
expect(result.geo_bounding_box.ignore_unmapped).to.be(true);
|
||||
});
|
||||
|
||||
it('should throw an error for scripted fields', function () {
|
||||
const node = nodeTypes.function.buildNode('geoBoundingBox', 'script number', params);
|
||||
expect(geoBoundingBox.toElasticsearchQuery)
|
||||
.withArgs(node, indexPattern).to.throwException(/Geo bounding box query does not support scripted fields/);
|
||||
});
|
||||
|
||||
it('should use a provided nested context to create a full field name', function () {
|
||||
const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params);
|
||||
const result = geoBoundingBox.toElasticsearchQuery(
|
||||
node,
|
||||
indexPattern,
|
||||
{},
|
||||
{ nested: { path: 'nestedField' } }
|
||||
);
|
||||
expect(result).to.have.property('geo_bounding_box');
|
||||
expect(result.geo_bounding_box).to.have.property('nestedField.geo');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import * as geoPolygon from '../geo_polygon';
|
||||
import { nodeTypes } from '../../node_types';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
|
||||
|
||||
let indexPattern;
|
||||
const points = [
|
||||
{
|
||||
lat: 69.77,
|
||||
lon: -171.56
|
||||
},
|
||||
{
|
||||
lat: 50.06,
|
||||
lon: -169.10
|
||||
},
|
||||
{
|
||||
lat: 69.16,
|
||||
lon: -125.85
|
||||
}
|
||||
];
|
||||
|
||||
describe('kuery functions', function () {
|
||||
|
||||
describe('geoPolygon', function () {
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
});
|
||||
|
||||
describe('buildNodeParams', function () {
|
||||
|
||||
it('should return an "arguments" param', function () {
|
||||
const result = geoPolygon.buildNodeParams('geo', points);
|
||||
expect(result).to.only.have.keys('arguments');
|
||||
});
|
||||
|
||||
it('arguments should contain the provided fieldName as a literal', function () {
|
||||
const result = geoPolygon.buildNodeParams('geo', points);
|
||||
const { arguments: [ fieldName ] } = result;
|
||||
|
||||
expect(fieldName).to.have.property('type', 'literal');
|
||||
expect(fieldName).to.have.property('value', 'geo');
|
||||
});
|
||||
|
||||
it('arguments should contain the provided points literal "lat, lon" string values', function () {
|
||||
const result = geoPolygon.buildNodeParams('geo', points);
|
||||
const { arguments: [ , ...args ] } = result;
|
||||
|
||||
args.forEach((param, index) => {
|
||||
expect(param).to.have.property('type', 'literal');
|
||||
const expectedPoint = points[index];
|
||||
const expectedLatLon = `${expectedPoint.lat}, ${expectedPoint.lon}`;
|
||||
expect(param.value).to.be(expectedLatLon);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
|
||||
it('should return an ES geo_polygon query representing the given node', function () {
|
||||
const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points);
|
||||
const result = geoPolygon.toElasticsearchQuery(node, indexPattern);
|
||||
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 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);
|
||||
const result = geoPolygon.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result.geo_polygon.ignore_unmapped).to.be(true);
|
||||
});
|
||||
|
||||
it('should throw an error for scripted fields', function () {
|
||||
const node = nodeTypes.function.buildNode('geoPolygon', 'script number', points);
|
||||
expect(geoPolygon.toElasticsearchQuery)
|
||||
.withArgs(node, indexPattern).to.throwException(/Geo polygon query does not support scripted fields/);
|
||||
});
|
||||
|
||||
it('should use a provided nested context to create a full field name', function () {
|
||||
const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points);
|
||||
const result = geoPolygon.toElasticsearchQuery(
|
||||
node,
|
||||
indexPattern,
|
||||
{},
|
||||
{ nested: { path: 'nestedField' } }
|
||||
);
|
||||
expect(result).to.have.property('geo_polygon');
|
||||
expect(result.geo_polygon).to.have.property('nestedField.geo');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,310 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import * as is from '../is';
|
||||
import { nodeTypes } from '../../node_types';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
|
||||
let indexPattern;
|
||||
|
||||
describe('kuery functions', function () {
|
||||
|
||||
describe('is', function () {
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
});
|
||||
|
||||
describe('buildNodeParams', function () {
|
||||
|
||||
it('fieldName and value should be required arguments', function () {
|
||||
expect(is.buildNodeParams).to.throwException(/fieldName is a required argument/);
|
||||
expect(is.buildNodeParams).withArgs('foo').to.throwException(/value is a required argument/);
|
||||
});
|
||||
|
||||
it('arguments should contain the provided fieldName and value as literals', function () {
|
||||
const { arguments: [fieldName, value] } = is.buildNodeParams('response', 200);
|
||||
|
||||
expect(fieldName).to.have.property('type', 'literal');
|
||||
expect(fieldName).to.have.property('value', 'response');
|
||||
|
||||
expect(value).to.have.property('type', 'literal');
|
||||
expect(value).to.have.property('value', 200);
|
||||
});
|
||||
|
||||
it('should detect wildcards in the provided arguments', function () {
|
||||
const { arguments: [fieldName, value] } = is.buildNodeParams('machine*', 'win*');
|
||||
|
||||
expect(fieldName).to.have.property('type', 'wildcard');
|
||||
expect(value).to.have.property('type', 'wildcard');
|
||||
});
|
||||
|
||||
it('should default to a non-phrase query', function () {
|
||||
const { arguments: [, , isPhrase] } = is.buildNodeParams('response', 200);
|
||||
expect(isPhrase.value).to.be(false);
|
||||
});
|
||||
|
||||
it('should allow specification of a phrase query', function () {
|
||||
const { arguments: [, , isPhrase] } = is.buildNodeParams('response', 200, true);
|
||||
expect(isPhrase.value).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
|
||||
it('should return an ES match_all query when fieldName and value are both "*"', function () {
|
||||
const expected = {
|
||||
match_all: {}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', '*', '*');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should return an ES multi_match query using default_field when fieldName is null', function () {
|
||||
const expected = {
|
||||
multi_match: {
|
||||
query: 200,
|
||||
type: 'best_fields',
|
||||
lenient: true,
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', null, 200);
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should return an ES query_string query using default_field when fieldName is null and value contains a wildcard', function () {
|
||||
const expected = {
|
||||
query_string: {
|
||||
query: 'jpg*',
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', null, 'jpg*');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should return an ES bool query with a sub-query for each field when fieldName is "*"', function () {
|
||||
const node = nodeTypes.function.buildNode('is', '*', 200);
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.have.property('bool');
|
||||
expect(result.bool.should).to.have.length(indexPattern.fields.length);
|
||||
});
|
||||
|
||||
it('should return an ES exists query when value is "*"', function () {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{ exists: { field: 'extension' } },
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', 'extension', '*');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should return an ES match query when a concrete fieldName and value are provided', 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, indexPattern);
|
||||
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: {
|
||||
should: [
|
||||
{ match_phrase: { extension: 'jpg' } },
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', 'extension', 'jpg', true);
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should create a query_string query for wildcard values', function () {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
query_string: {
|
||||
fields: ['extension'],
|
||||
query: 'jpg*'
|
||||
}
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', 'extension', 'jpg*');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support scripted fields', function () {
|
||||
const node = nodeTypes.function.buildNode('is', 'script string', 'foo');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result.bool.should[0]).to.have.key('script');
|
||||
});
|
||||
|
||||
it('should support date fields without a dateFormat provided', function () {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: '2018-04-03T19:04:17',
|
||||
lte: '2018-04-03T19:04:17',
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', '@timestamp', '"2018-04-03T19:04:17"');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support date fields with a dateFormat provided', function () {
|
||||
const config = { dateFormatTZ: 'America/Phoenix' };
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: '2018-04-03T19:04:17',
|
||||
lte: '2018-04-03T19:04:17',
|
||||
time_zone: 'America/Phoenix',
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', '@timestamp', '"2018-04-03T19:04:17"');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern, config);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should use a provided nested context to create a full field name', function () {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{ match: { 'nestedField.extension': 'jpg' } },
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', 'extension', 'jpg');
|
||||
const result = is.toElasticsearchQuery(
|
||||
node,
|
||||
indexPattern,
|
||||
{},
|
||||
{ nested: { path: 'nestedField' } }
|
||||
);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should support wildcard field names', function () {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{ match: { extension: 'jpg' } },
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', 'ext*', 'jpg');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should automatically add a nested query when a wildcard field name covers a nested field', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
nested: {
|
||||
path: 'nestedField.nestedChild',
|
||||
query: {
|
||||
match: {
|
||||
'nestedField.nestedChild.doublyNestedChild': 'foo'
|
||||
}
|
||||
},
|
||||
score_mode: 'none'
|
||||
}
|
||||
}
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const node = nodeTypes.function.buildNode('is', '*doublyNested*', 'foo');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { nodeTypes } from '../../../node_types';
|
||||
import indexPatternResponse from '../../../../__fixtures__/index_pattern_response.json';
|
||||
import { getFullFieldNameNode } from '../../utils/get_full_field_name_node';
|
||||
|
||||
let indexPattern;
|
||||
|
||||
describe('getFullFieldNameNode', function () {
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
});
|
||||
|
||||
it('should return unchanged name node if no nested path is passed in', () => {
|
||||
const nameNode = nodeTypes.literal.buildNode('notNested');
|
||||
const result = getFullFieldNameNode(nameNode, indexPattern);
|
||||
expect(result).to.eql(nameNode);
|
||||
});
|
||||
|
||||
it('should add the nested path if it is valid according to the index pattern', () => {
|
||||
const nameNode = nodeTypes.literal.buildNode('child');
|
||||
const result = getFullFieldNameNode(nameNode, indexPattern, 'nestedField');
|
||||
expect(result).to.eql(nodeTypes.literal.buildNode('nestedField.child'));
|
||||
});
|
||||
|
||||
it('should throw an error if a path is provided for a non-nested field', () => {
|
||||
const nameNode = nodeTypes.literal.buildNode('os');
|
||||
expect(getFullFieldNameNode)
|
||||
.withArgs(nameNode, indexPattern, 'machine')
|
||||
.to
|
||||
.throwException(/machine.os is not a nested field but is in nested group "machine" in the KQL expression/);
|
||||
});
|
||||
|
||||
it('should throw an error if a nested field is not passed with a path', () => {
|
||||
const nameNode = nodeTypes.literal.buildNode('nestedField.child');
|
||||
expect(getFullFieldNameNode)
|
||||
.withArgs(nameNode, indexPattern)
|
||||
.to
|
||||
.throwException(/nestedField.child is a nested field, but is not in a nested group in the KQL expression./);
|
||||
});
|
||||
|
||||
it('should throw an error if a nested field is passed with the wrong path', () => {
|
||||
const nameNode = nodeTypes.literal.buildNode('nestedChild.doublyNestedChild');
|
||||
expect(getFullFieldNameNode)
|
||||
.withArgs(nameNode, indexPattern, 'nestedField')
|
||||
.to
|
||||
// eslint-disable-next-line max-len
|
||||
.throwException(/Nested field nestedField.nestedChild.doublyNestedChild is being queried with the incorrect nested path. The correct path is nestedField.nestedChild/);
|
||||
});
|
||||
|
||||
it('should skip error checking for wildcard names', () => {
|
||||
const nameNode = nodeTypes.wildcard.buildNode('nested*');
|
||||
const result = getFullFieldNameNode(nameNode, indexPattern);
|
||||
expect(result).to.eql(nameNode);
|
||||
});
|
||||
|
||||
it('should skip error checking if no index pattern is passed in', () => {
|
||||
const nameNode = nodeTypes.literal.buildNode('os');
|
||||
expect(getFullFieldNameNode)
|
||||
.withArgs(nameNode, null, 'machine')
|
||||
.to
|
||||
.not
|
||||
.throwException();
|
||||
|
||||
const result = getFullFieldNameNode(nameNode, null, 'machine');
|
||||
expect(result).to.eql(nodeTypes.literal.buildNode('machine.os'));
|
||||
});
|
||||
|
||||
});
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import * as functionType from '../function';
|
||||
import _ from 'lodash';
|
||||
import expect from '@kbn/expect';
|
||||
import * as isFunction from '../../functions/is';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
|
||||
import { nodeTypes } from '../../node_types';
|
||||
|
||||
describe('kuery node types', function () {
|
||||
|
||||
describe('function', function () {
|
||||
|
||||
let indexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
});
|
||||
|
||||
describe('buildNode', function () {
|
||||
|
||||
it('should return a node representing the given kuery function', function () {
|
||||
const result = functionType.buildNode('is', 'extension', 'jpg');
|
||||
expect(result).to.have.property('type', 'function');
|
||||
expect(result).to.have.property('function', 'is');
|
||||
expect(result).to.have.property('arguments');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('buildNodeWithArgumentNodes', function () {
|
||||
|
||||
it('should return a function node with the given argument list untouched', function () {
|
||||
const fieldNameLiteral = nodeTypes.literal.buildNode('extension');
|
||||
const valueLiteral = nodeTypes.literal.buildNode('jpg');
|
||||
const argumentNodes = [fieldNameLiteral, valueLiteral];
|
||||
const result = functionType.buildNodeWithArgumentNodes('is', argumentNodes);
|
||||
|
||||
expect(result).to.have.property('type', 'function');
|
||||
expect(result).to.have.property('function', 'is');
|
||||
expect(result).to.have.property('arguments');
|
||||
expect(result.arguments).to.be(argumentNodes);
|
||||
expect(result.arguments).to.eql(argumentNodes);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
|
||||
it('should return the given function type\'s ES query representation', function () {
|
||||
const node = functionType.buildNode('is', 'extension', 'jpg');
|
||||
const expected = isFunction.toElasticsearchQuery(node, indexPattern);
|
||||
const result = functionType.toElasticsearchQuery(node, indexPattern);
|
||||
expect(_.isEqual(expected, result)).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import * as namedArg from '../named_arg';
|
||||
import { nodeTypes } from '../../node_types';
|
||||
|
||||
describe('kuery node types', function () {
|
||||
|
||||
describe('named arg', function () {
|
||||
|
||||
describe('buildNode', function () {
|
||||
|
||||
it('should return a node representing a named argument with the given value', function () {
|
||||
const result = namedArg.buildNode('fieldName', 'foo');
|
||||
expect(result).to.have.property('type', 'namedArg');
|
||||
expect(result).to.have.property('name', 'fieldName');
|
||||
expect(result).to.have.property('value');
|
||||
|
||||
const literalValue = result.value;
|
||||
expect(literalValue).to.have.property('type', 'literal');
|
||||
expect(literalValue).to.have.property('value', 'foo');
|
||||
});
|
||||
|
||||
it('should support literal nodes as values', function () {
|
||||
const value = nodeTypes.literal.buildNode('foo');
|
||||
const result = namedArg.buildNode('fieldName', value);
|
||||
expect(result.value).to.be(value);
|
||||
expect(result.value).to.eql(value);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
|
||||
it('should return the argument value represented by the given node', function () {
|
||||
const node = namedArg.buildNode('fieldName', 'foo');
|
||||
const result = namedArg.toElasticsearchQuery(node);
|
||||
expect(result).to.be('foo');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import * as wildcard from '../wildcard';
|
||||
|
||||
describe('kuery node types', function () {
|
||||
|
||||
describe('wildcard', function () {
|
||||
|
||||
describe('buildNode', function () {
|
||||
|
||||
it('should accept a string argument representing a wildcard string', function () {
|
||||
const wildcardValue = `foo${wildcard.wildcardSymbol}bar`;
|
||||
const result = wildcard.buildNode(wildcardValue);
|
||||
expect(result).to.have.property('type', 'wildcard');
|
||||
expect(result).to.have.property('value', wildcardValue);
|
||||
});
|
||||
|
||||
it('should accept and parse a wildcard string', function () {
|
||||
const result = wildcard.buildNode('foo*bar');
|
||||
expect(result).to.have.property('type', 'wildcard');
|
||||
expect(result.value).to.be(`foo${wildcard.wildcardSymbol}bar`);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
|
||||
it('should return the string representation of the wildcard literal', function () {
|
||||
const node = wildcard.buildNode('foo*bar');
|
||||
const result = wildcard.toElasticsearchQuery(node);
|
||||
expect(result).to.be('foo*bar');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('toQueryStringQuery', function () {
|
||||
|
||||
it('should return the string representation of the wildcard literal', function () {
|
||||
const node = wildcard.buildNode('foo*bar');
|
||||
const result = wildcard.toQueryStringQuery(node);
|
||||
expect(result).to.be('foo*bar');
|
||||
});
|
||||
|
||||
it('should escape query_string query special characters other than wildcard', function () {
|
||||
const node = wildcard.buildNode('+foo*bar');
|
||||
const result = wildcard.toQueryStringQuery(node);
|
||||
expect(result).to.be('\\+foo*bar');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('test', function () {
|
||||
|
||||
it('should return a boolean indicating whether the string matches the given wildcard node', function () {
|
||||
const node = wildcard.buildNode('foo*bar');
|
||||
expect(wildcard.test(node, 'foobar')).to.be(true);
|
||||
expect(wildcard.test(node, 'foobazbar')).to.be(true);
|
||||
expect(wildcard.test(node, 'foobar')).to.be(true);
|
||||
|
||||
expect(wildcard.test(node, 'fooqux')).to.be(false);
|
||||
expect(wildcard.test(node, 'bazbar')).to.be(false);
|
||||
});
|
||||
|
||||
it('should return a true even when the string has newlines or tabs', function () {
|
||||
const node = wildcard.buildNode('foo*bar');
|
||||
expect(wildcard.test(node, 'foo\nbar')).to.be(true);
|
||||
expect(wildcard.test(node, 'foo\tbar')).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasLeadingWildcard', function () {
|
||||
it('should determine whether a wildcard node contains a leading wildcard', function () {
|
||||
const node = wildcard.buildNode('foo*bar');
|
||||
expect(wildcard.hasLeadingWildcard(node)).to.be(false);
|
||||
|
||||
const leadingWildcardNode = wildcard.buildNode('*foobar');
|
||||
expect(wildcard.hasLeadingWildcard(leadingWildcardNode)).to.be(true);
|
||||
});
|
||||
|
||||
// Lone wildcards become exists queries, so we aren't worried about their performance
|
||||
it('should not consider a lone wildcard to be a leading wildcard', function () {
|
||||
const leadingWildcardNode = wildcard.buildNode('*');
|
||||
expect(wildcard.hasLeadingWildcard(leadingWildcardNode)).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { getTimeZoneFromSettings } from '../get_time_zone_from_settings';
|
||||
|
||||
describe('get timezone from settings', function () {
|
||||
|
||||
it('should return the config timezone if the time zone is set', function () {
|
||||
const result = getTimeZoneFromSettings('America/Chicago');
|
||||
expect(result).to.eql('America/Chicago');
|
||||
});
|
||||
|
||||
it('should return the system timezone if the time zone is set to "Browser"', function () {
|
||||
const result = getTimeZoneFromSettings('Browser');
|
||||
expect(result).to.not.equal('Browser');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { pick, get, reduce, map } from 'lodash';
|
||||
|
||||
/** @deprecated
|
||||
* @see src/plugins/data/public/es_query/filters/phrase_filter.ts
|
||||
* Code was already moved into src/plugins/data/public.
|
||||
* This method will be removed after moving 'es_query' into new platform
|
||||
* */
|
||||
export const getConvertedValueForField = (field, value) => {
|
||||
if (typeof value !== 'boolean' && field.type === 'boolean') {
|
||||
if ([1, 'true'].includes(value)) {
|
||||
return true;
|
||||
} else if ([0, 'false'].includes(value)) {
|
||||
return false;
|
||||
} else {
|
||||
throw new Error(`${value} is not a valid boolean value for boolean field ${field.name}`);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
/** @deprecated
|
||||
* @see src/plugins/data/public/es_query/filters/phrase_filter.ts
|
||||
* Code was already moved into src/plugins/data/public.
|
||||
* This method will be removed after moving 'es_query' into new platform
|
||||
* */
|
||||
export const buildInlineScriptForPhraseFilter = (scriptedField) => {
|
||||
// We must wrap painless scripts in a lambda in case they're more than a simple expression
|
||||
if (scriptedField.lang === 'painless') {
|
||||
return (
|
||||
`boolean compare(Supplier s, def v) {return s.get() == v;}` +
|
||||
`compare(() -> { ${scriptedField.script} }, params.value);`
|
||||
);
|
||||
} else {
|
||||
return `(${scriptedField.script}) == value`;
|
||||
}
|
||||
};
|
||||
|
||||
/** @deprecated
|
||||
* @see src/plugins/data/public/es_query/filters/phrase_filter.ts
|
||||
* Code was already moved into src/plugins/data/public.
|
||||
* This method will be removed after moving 'es_query' into new platform
|
||||
* */
|
||||
export function getPhraseScript(field, value) {
|
||||
const convertedValue = getConvertedValueForField(field, value);
|
||||
const script = buildInlineScriptForPhraseFilter(field);
|
||||
|
||||
return {
|
||||
script: {
|
||||
source: script,
|
||||
lang: field.lang,
|
||||
params: {
|
||||
value: convertedValue,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** @deprecated
|
||||
* @see src/plugins/data/public/es_query/filters/range_filter.ts
|
||||
* Code was already moved into src/plugins/data/public.
|
||||
* This method will be removed after moving 'kuery' into new platform
|
||||
* */
|
||||
export function getRangeScript(field, params) {
|
||||
const operators = {
|
||||
gt: '>',
|
||||
gte: '>=',
|
||||
lte: '<=',
|
||||
lt: '<',
|
||||
};
|
||||
const comparators = {
|
||||
gt: 'boolean gt(Supplier s, def v) {return s.get() > v}',
|
||||
gte: 'boolean gte(Supplier s, def v) {return s.get() >= v}',
|
||||
lte: 'boolean lte(Supplier s, def v) {return s.get() <= v}',
|
||||
lt: 'boolean lt(Supplier s, def v) {return s.get() < v}',
|
||||
};
|
||||
|
||||
const dateComparators = {
|
||||
gt: 'boolean gt(Supplier s, def v) {return s.get().toInstant().isAfter(Instant.parse(v))}',
|
||||
gte: 'boolean gte(Supplier s, def v) {return !s.get().toInstant().isBefore(Instant.parse(v))}',
|
||||
lte: 'boolean lte(Supplier s, def v) {return !s.get().toInstant().isAfter(Instant.parse(v))}',
|
||||
lt: 'boolean lt(Supplier s, def v) {return s.get().toInstant().isBefore(Instant.parse(v))}',
|
||||
};
|
||||
|
||||
const knownParams = pick(params, (val, key) => {
|
||||
return key in operators;
|
||||
});
|
||||
let script = map(knownParams, (val, key) => {
|
||||
return '(' + field.script + ')' + get(operators, key) + key;
|
||||
}).join(' && ');
|
||||
|
||||
// We must wrap painless scripts in a lambda in case they're more than a simple expression
|
||||
if (field.lang === 'painless') {
|
||||
const comp = field.type === 'date' ? dateComparators : comparators;
|
||||
const currentComparators = reduce(
|
||||
knownParams,
|
||||
(acc, val, key) => acc.concat(get(comp, key)),
|
||||
[]
|
||||
).join(' ');
|
||||
|
||||
const comparisons = map(knownParams, (val, key) => {
|
||||
return `${key}(() -> { ${field.script} }, params.${key})`;
|
||||
}).join(' && ');
|
||||
|
||||
script = `${currentComparators}${comparisons}`;
|
||||
}
|
||||
|
||||
return {
|
||||
script: {
|
||||
source: script,
|
||||
params: knownParams,
|
||||
lang: field.lang,
|
||||
},
|
||||
};
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
const detectedTimezone = moment.tz.guess();
|
||||
|
||||
export function getTimeZoneFromSettings(dateFormatTZ) {
|
||||
if (dateFormatTZ === 'Browser') {
|
||||
return detectedTimezone;
|
||||
}
|
||||
return dateFormatTZ;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export * from './get_time_zone_from_settings';
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
const { resolve } = require('path');
|
||||
|
||||
const getopts = require('getopts');
|
||||
const del = require('del');
|
||||
const supportsColor = require('supports-color');
|
||||
const { ToolingLog, withProcRunner, pickLevelFromFlags } = require('@kbn/dev-utils');
|
||||
|
||||
const ROOT_DIR = resolve(__dirname, '..');
|
||||
const BUILD_DIR = resolve(ROOT_DIR, 'target');
|
||||
|
||||
const padRight = (width, str) =>
|
||||
str.length >= width ? str : `${str}${' '.repeat(width - str.length)}`;
|
||||
|
||||
const unknownFlags = [];
|
||||
const flags = getopts(process.argv, {
|
||||
boolean: ['watch', 'help', 'source-maps'],
|
||||
unknown(name) {
|
||||
unknownFlags.push(name);
|
||||
},
|
||||
});
|
||||
|
||||
const log = new ToolingLog({
|
||||
level: pickLevelFromFlags(flags),
|
||||
writeTo: process.stdout,
|
||||
});
|
||||
|
||||
if (unknownFlags.length) {
|
||||
log.error(`Unknown flag(s): ${unknownFlags.join(', ')}`);
|
||||
flags.help = true;
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
if (flags.help) {
|
||||
log.info(`
|
||||
Simple build tool for @kbn/es-query package
|
||||
|
||||
--watch Run in watch mode
|
||||
--source-maps Include sourcemaps
|
||||
--help Show this message
|
||||
`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
withProcRunner(log, async proc => {
|
||||
log.info('Deleting old output');
|
||||
await del(BUILD_DIR);
|
||||
|
||||
const cwd = ROOT_DIR;
|
||||
const env = { ...process.env };
|
||||
if (supportsColor.stdout) {
|
||||
env.FORCE_COLOR = 'true';
|
||||
}
|
||||
|
||||
log.info(`Starting babel and typescript${flags.watch ? ' in watch mode' : ''}`);
|
||||
await Promise.all([
|
||||
...['public', 'server'].map(subTask =>
|
||||
proc.run(padRight(12, `babel:${subTask}`), {
|
||||
cmd: 'babel',
|
||||
args: [
|
||||
'src',
|
||||
'--config-file',
|
||||
require.resolve('../babel.config.js'),
|
||||
'--out-dir',
|
||||
resolve(BUILD_DIR, subTask),
|
||||
'--extensions',
|
||||
'.js,.ts,.tsx',
|
||||
...(flags.watch ? ['--watch'] : ['--quiet']),
|
||||
...(flags['source-maps'] ? ['--source-map', 'inline'] : []),
|
||||
],
|
||||
wait: true,
|
||||
cwd,
|
||||
env: {
|
||||
...env,
|
||||
BABEL_ENV: subTask,
|
||||
},
|
||||
})
|
||||
),
|
||||
]);
|
||||
|
||||
log.success('Complete');
|
||||
}).catch(error => {
|
||||
log.error(error);
|
||||
process.exit(1);
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.browser.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./target/public"
|
||||
},
|
||||
"include": [
|
||||
"index.d.ts",
|
||||
"src/**/*.ts"
|
||||
]
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./target/server"
|
||||
},
|
||||
"include": [
|
||||
"index.d.ts",
|
||||
"src/**/*.ts"
|
||||
]
|
||||
}
|
|
@ -846,7 +846,7 @@ export class SavedObjectsClient {
|
|||
bulkUpdate<T extends SavedObjectAttributes>(objects?: SavedObjectsBulkUpdateObject[]): Promise<SavedObjectsBatchResponse<SavedObjectAttributes>>;
|
||||
create: <T extends SavedObjectAttributes>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>>;
|
||||
delete: (type: string, id: string) => Promise<{}>;
|
||||
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectsFindOptions, "search" | "filter" | "type" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "page" | "perPage" | "fields">) => Promise<SavedObjectsFindResponsePublic<T>>;
|
||||
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectsFindOptions, "search" | "filter" | "type" | "fields" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "page" | "perPage">) => Promise<SavedObjectsFindResponsePublic<T>>;
|
||||
get: <T extends SavedObjectAttributes>(type: string, id: string) => Promise<SimpleSavedObject<T>>;
|
||||
update<T extends SavedObjectAttributes>(type: string, id: string, attributes: T, { version, migrationVersion, references }?: SavedObjectsUpdateOptions): Promise<SimpleSavedObject<T>>;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { fromKueryExpression } from '@kbn/es-query';
|
||||
import { esKuery } from '../../../../../plugins/data/server';
|
||||
|
||||
import { validateFilterKueryNode, validateConvertFilterToKueryNode } from './filter_utils';
|
||||
|
||||
|
@ -64,7 +64,7 @@ describe('Filter Utils', () => {
|
|||
test('Validate a simple filter', () => {
|
||||
expect(
|
||||
validateConvertFilterToKueryNode(['foo'], 'foo.attributes.title: "best"', mockMappings)
|
||||
).toEqual(fromKueryExpression('foo.title: "best"'));
|
||||
).toEqual(esKuery.fromKueryExpression('foo.title: "best"'));
|
||||
});
|
||||
test('Assemble filter kuery node saved object attributes with one saved object type', () => {
|
||||
expect(
|
||||
|
@ -74,7 +74,7 @@ describe('Filter Utils', () => {
|
|||
mockMappings
|
||||
)
|
||||
).toEqual(
|
||||
fromKueryExpression(
|
||||
esKuery.fromKueryExpression(
|
||||
'(type: foo and updatedAt: 5678654567) and foo.bytes > 1000 and foo.bytes < 8000 and foo.title: "best" and (foo.description: t* or foo.description :*)'
|
||||
)
|
||||
);
|
||||
|
@ -88,7 +88,7 @@ describe('Filter Utils', () => {
|
|||
mockMappings
|
||||
)
|
||||
).toEqual(
|
||||
fromKueryExpression(
|
||||
esKuery.fromKueryExpression(
|
||||
'(type: foo and updatedAt: 5678654567) and foo.bytes > 1000 and foo.bytes < 8000 and foo.title: "best" and (foo.description: t* or foo.description :*)'
|
||||
)
|
||||
);
|
||||
|
@ -102,7 +102,7 @@ describe('Filter Utils', () => {
|
|||
mockMappings
|
||||
)
|
||||
).toEqual(
|
||||
fromKueryExpression(
|
||||
esKuery.fromKueryExpression(
|
||||
'((type: bar and updatedAt: 5678654567) or (type: foo and updatedAt: 5678654567)) and foo.bytes > 1000 and foo.bytes < 8000 and foo.title: "best" and (foo.description: t* or bar.description :*)'
|
||||
)
|
||||
);
|
||||
|
@ -130,7 +130,7 @@ describe('Filter Utils', () => {
|
|||
describe('#validateFilterKueryNode', () => {
|
||||
test('Validate filter query through KueryNode - happy path', () => {
|
||||
const validationObject = validateFilterKueryNode(
|
||||
fromKueryExpression(
|
||||
esKuery.fromKueryExpression(
|
||||
'foo.updatedAt: 5678654567 and foo.attributes.bytes > 1000 and foo.attributes.bytes < 8000 and foo.attributes.title: "best" and (foo.attributes.description: t* or foo.attributes.description :*)'
|
||||
),
|
||||
['foo'],
|
||||
|
@ -185,7 +185,7 @@ describe('Filter Utils', () => {
|
|||
|
||||
test('Return Error if key is not wrapper by a saved object type', () => {
|
||||
const validationObject = validateFilterKueryNode(
|
||||
fromKueryExpression(
|
||||
esKuery.fromKueryExpression(
|
||||
'updatedAt: 5678654567 and foo.attributes.bytes > 1000 and foo.attributes.bytes < 8000 and foo.attributes.title: "best" and (foo.attributes.description: t* or foo.attributes.description :*)'
|
||||
),
|
||||
['foo'],
|
||||
|
@ -240,7 +240,7 @@ describe('Filter Utils', () => {
|
|||
|
||||
test('Return Error if key of a saved object type is not wrapped with attributes', () => {
|
||||
const validationObject = validateFilterKueryNode(
|
||||
fromKueryExpression(
|
||||
esKuery.fromKueryExpression(
|
||||
'foo.updatedAt: 5678654567 and foo.attributes.bytes > 1000 and foo.bytes < 8000 and foo.attributes.title: "best" and (foo.attributes.description: t* or foo.description :*)'
|
||||
),
|
||||
['foo'],
|
||||
|
@ -297,7 +297,7 @@ describe('Filter Utils', () => {
|
|||
|
||||
test('Return Error if filter is not using an allowed type', () => {
|
||||
const validationObject = validateFilterKueryNode(
|
||||
fromKueryExpression(
|
||||
esKuery.fromKueryExpression(
|
||||
'bar.updatedAt: 5678654567 and foo.attributes.bytes > 1000 and foo.attributes.bytes < 8000 and foo.attributes.title: "best" and (foo.attributes.description: t* or foo.attributes.description :*)'
|
||||
),
|
||||
['foo'],
|
||||
|
@ -352,7 +352,7 @@ describe('Filter Utils', () => {
|
|||
|
||||
test('Return Error if filter is using an non-existing key in the index patterns of the saved object type', () => {
|
||||
const validationObject = validateFilterKueryNode(
|
||||
fromKueryExpression(
|
||||
esKuery.fromKueryExpression(
|
||||
'foo.updatedAt33: 5678654567 and foo.attributes.bytes > 1000 and foo.attributes.bytes < 8000 and foo.attributes.header: "best" and (foo.attributes.description: t* or foo.attributes.description :*)'
|
||||
),
|
||||
['foo'],
|
||||
|
@ -408,7 +408,7 @@ describe('Filter Utils', () => {
|
|||
|
||||
test('Return Error if filter is using an non-existing key null key', () => {
|
||||
const validationObject = validateFilterKueryNode(
|
||||
fromKueryExpression('foo.attributes.description: hello AND bye'),
|
||||
esKuery.fromKueryExpression('foo.attributes.description: hello AND bye'),
|
||||
['foo'],
|
||||
mockMappings
|
||||
);
|
||||
|
|
|
@ -17,18 +17,18 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { fromKueryExpression, KueryNode, nodeTypes } from '@kbn/es-query';
|
||||
import { get, set } from 'lodash';
|
||||
import { SavedObjectsErrorHelpers } from './errors';
|
||||
import { IndexMapping } from '../../mappings';
|
||||
import { esKuery } from '../../../../../plugins/data/server';
|
||||
|
||||
export const validateConvertFilterToKueryNode = (
|
||||
allowedTypes: string[],
|
||||
filter: string,
|
||||
indexMapping: IndexMapping
|
||||
): KueryNode => {
|
||||
): esKuery.KueryNode | undefined => {
|
||||
if (filter && filter.length > 0 && indexMapping) {
|
||||
const filterKueryNode = fromKueryExpression(filter);
|
||||
const filterKueryNode = esKuery.fromKueryExpression(filter);
|
||||
|
||||
const validationFilterKuery = validateFilterKueryNode(
|
||||
filterKueryNode,
|
||||
|
@ -54,7 +54,7 @@ export const validateConvertFilterToKueryNode = (
|
|||
|
||||
validationFilterKuery.forEach(item => {
|
||||
const path: string[] = item.astPath.length === 0 ? [] : item.astPath.split('.');
|
||||
const existingKueryNode: KueryNode =
|
||||
const existingKueryNode: esKuery.KueryNode =
|
||||
path.length === 0 ? filterKueryNode : get(filterKueryNode, path);
|
||||
if (item.isSavedObjectAttr) {
|
||||
existingKueryNode.arguments[0].value = existingKueryNode.arguments[0].value.split('.')[1];
|
||||
|
@ -63,8 +63,8 @@ export const validateConvertFilterToKueryNode = (
|
|||
set(
|
||||
filterKueryNode,
|
||||
path,
|
||||
nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('is', 'type', itemType[0]),
|
||||
esKuery.nodeTypes.function.buildNode('and', [
|
||||
esKuery.nodeTypes.function.buildNode('is', 'type', itemType[0]),
|
||||
existingKueryNode,
|
||||
])
|
||||
);
|
||||
|
@ -79,7 +79,6 @@ export const validateConvertFilterToKueryNode = (
|
|||
});
|
||||
return filterKueryNode;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
interface ValidateFilterKueryNode {
|
||||
|
@ -91,41 +90,44 @@ interface ValidateFilterKueryNode {
|
|||
}
|
||||
|
||||
export const validateFilterKueryNode = (
|
||||
astFilter: KueryNode,
|
||||
astFilter: esKuery.KueryNode,
|
||||
types: string[],
|
||||
indexMapping: IndexMapping,
|
||||
storeValue: boolean = false,
|
||||
path: string = 'arguments'
|
||||
): ValidateFilterKueryNode[] => {
|
||||
return astFilter.arguments.reduce((kueryNode: string[], ast: KueryNode, index: number) => {
|
||||
if (ast.arguments) {
|
||||
const myPath = `${path}.${index}`;
|
||||
return [
|
||||
...kueryNode,
|
||||
...validateFilterKueryNode(
|
||||
ast,
|
||||
types,
|
||||
indexMapping,
|
||||
ast.type === 'function' && ['is', 'range'].includes(ast.function),
|
||||
`${myPath}.arguments`
|
||||
),
|
||||
];
|
||||
}
|
||||
if (storeValue && index === 0) {
|
||||
const splitPath = path.split('.');
|
||||
return [
|
||||
...kueryNode,
|
||||
{
|
||||
astPath: splitPath.slice(0, splitPath.length - 1).join('.'),
|
||||
error: hasFilterKeyError(ast.value, types, indexMapping),
|
||||
isSavedObjectAttr: isSavedObjectAttr(ast.value, indexMapping),
|
||||
key: ast.value,
|
||||
type: getType(ast.value),
|
||||
},
|
||||
];
|
||||
}
|
||||
return kueryNode;
|
||||
}, []);
|
||||
return astFilter.arguments.reduce(
|
||||
(kueryNode: string[], ast: esKuery.KueryNode, index: number) => {
|
||||
if (ast.arguments) {
|
||||
const myPath = `${path}.${index}`;
|
||||
return [
|
||||
...kueryNode,
|
||||
...validateFilterKueryNode(
|
||||
ast,
|
||||
types,
|
||||
indexMapping,
|
||||
ast.type === 'function' && ['is', 'range'].includes(ast.function),
|
||||
`${myPath}.arguments`
|
||||
),
|
||||
];
|
||||
}
|
||||
if (storeValue && index === 0) {
|
||||
const splitPath = path.split('.');
|
||||
return [
|
||||
...kueryNode,
|
||||
{
|
||||
astPath: splitPath.slice(0, splitPath.length - 1).join('.'),
|
||||
error: hasFilterKeyError(ast.value, types, indexMapping),
|
||||
isSavedObjectAttr: isSavedObjectAttr(ast.value, indexMapping),
|
||||
key: ast.value,
|
||||
type: getType(ast.value),
|
||||
},
|
||||
];
|
||||
}
|
||||
return kueryNode;
|
||||
},
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
||||
const getType = (key: string | undefined | null) =>
|
||||
|
|
|
@ -1306,8 +1306,7 @@ describe('SavedObjectsRepository', () => {
|
|||
type: 'foo',
|
||||
id: '1',
|
||||
},
|
||||
indexPattern: undefined,
|
||||
kueryNode: null,
|
||||
kueryNode: undefined,
|
||||
};
|
||||
|
||||
await savedObjectsRepository.find(relevantOpts);
|
||||
|
|
|
@ -448,11 +448,11 @@ export class SavedObjectsRepository {
|
|||
}
|
||||
|
||||
let kueryNode;
|
||||
|
||||
try {
|
||||
kueryNode =
|
||||
filter && filter !== ''
|
||||
? validateConvertFilterToKueryNode(allowedTypes, filter, this._mappings)
|
||||
: null;
|
||||
if (filter) {
|
||||
kueryNode = validateConvertFilterToKueryNode(allowedTypes, filter, this._mappings);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.name === 'KQLSyntaxError') {
|
||||
throw SavedObjectsErrorHelpers.createBadRequestError('KQLSyntaxError: ' + e.message);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { toElasticsearchQuery, KueryNode } from '@kbn/es-query';
|
||||
import { esKuery } from '../../../../../../plugins/data/server';
|
||||
|
||||
import { getRootPropertiesObjects, IndexMapping } from '../../../mappings';
|
||||
import { SavedObjectsSchema } from '../../../schema';
|
||||
|
@ -91,7 +91,7 @@ interface QueryParams {
|
|||
searchFields?: string[];
|
||||
defaultSearchOperator?: string;
|
||||
hasReference?: HasReferenceQueryParams;
|
||||
kueryNode?: KueryNode;
|
||||
kueryNode?: esKuery.KueryNode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,7 +111,7 @@ export function getQueryParams({
|
|||
const types = getTypes(mappings, type);
|
||||
const bool: any = {
|
||||
filter: [
|
||||
...(kueryNode != null ? [toElasticsearchQuery(kueryNode)] : []),
|
||||
...(kueryNode != null ? [esKuery.toElasticsearchQuery(kueryNode)] : []),
|
||||
{
|
||||
bool: {
|
||||
must: hasReference
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { KueryNode } from '@kbn/es-query';
|
||||
import Boom from 'boom';
|
||||
|
||||
import { IndexMapping } from '../../../mappings';
|
||||
import { SavedObjectsSchema } from '../../../schema';
|
||||
import { getQueryParams } from './query_params';
|
||||
import { getSortingParams } from './sorting_params';
|
||||
import { esKuery } from '../../../../../../plugins/data/server';
|
||||
|
||||
interface GetSearchDslOptions {
|
||||
type: string | string[];
|
||||
|
@ -37,7 +37,7 @@ interface GetSearchDslOptions {
|
|||
type: string;
|
||||
id: string;
|
||||
};
|
||||
kueryNode?: KueryNode;
|
||||
kueryNode?: esKuery.KueryNode;
|
||||
}
|
||||
|
||||
export function getSearchDsl(
|
||||
|
|
|
@ -18,11 +18,8 @@
|
|||
*/
|
||||
|
||||
import dateMath from '@elastic/datemath';
|
||||
import { doesKueryExpressionHaveLuceneSyntaxError } from '@kbn/es-query';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiFlexGroup,
|
||||
|
@ -42,9 +39,9 @@ import {
|
|||
Query,
|
||||
PersistedLog,
|
||||
getQueryLog,
|
||||
esKuery,
|
||||
} from '../../../../../../../plugins/data/public';
|
||||
import { useKibana, toMountPoint } from '../../../../../../../plugins/kibana_react/public';
|
||||
|
||||
import { IndexPattern } from '../../../index_patterns';
|
||||
import { QueryBarInput } from './query_bar_input';
|
||||
|
||||
|
@ -300,7 +297,7 @@ function QueryBarTopRowUI(props: Props) {
|
|||
language === 'kuery' &&
|
||||
typeof query === 'string' &&
|
||||
(!storage || !storage.get('kibana.luceneSyntaxWarningOptOut')) &&
|
||||
doesKueryExpressionHaveLuceneSyntaxError(query)
|
||||
esKuery.doesKueryExpressionHaveLuceneSyntaxError(query)
|
||||
) {
|
||||
const toast = notifications!.toasts.addWarning({
|
||||
title: intl.formatMessage({
|
||||
|
|
|
@ -83,7 +83,7 @@ export function getTimelionRequestHandler(dependencies: TimelionVisualizationDep
|
|||
sheet: [expression],
|
||||
extended: {
|
||||
es: {
|
||||
filter: esQuery.buildEsQuery(null, query, filters, esQueryConfigs),
|
||||
filter: esQuery.buildEsQuery(undefined, query, filters, esQueryConfigs),
|
||||
},
|
||||
},
|
||||
time: {
|
||||
|
|
|
@ -22,7 +22,6 @@ import React, { Component } from 'react';
|
|||
import * as Rx from 'rxjs';
|
||||
import { share } from 'rxjs/operators';
|
||||
import { isEqual, isEmpty, debounce } from 'lodash';
|
||||
import { fromKueryExpression } from '@kbn/es-query';
|
||||
import { VisEditorVisualization } from './vis_editor_visualization';
|
||||
import { Visualization } from './visualization';
|
||||
import { VisPicker } from './vis_picker';
|
||||
|
@ -30,6 +29,7 @@ import { PanelConfig } from './panel_config';
|
|||
import { createBrushHandler } from '../lib/create_brush_handler';
|
||||
import { fetchFields } from '../lib/fetch_fields';
|
||||
import { extractIndexPatterns } from '../../common/extract_index_patterns';
|
||||
import { esKuery } from '../../../../../plugins/data/public';
|
||||
|
||||
import { npStart } from 'ui/new_platform';
|
||||
|
||||
|
@ -88,7 +88,7 @@ export class VisEditor extends Component {
|
|||
if (filterQuery && filterQuery.language === 'kuery') {
|
||||
try {
|
||||
const queryOptions = this.coreContext.uiSettings.get('query:allowLeadingWildcards');
|
||||
fromKueryExpression(filterQuery.query, { allowLeadingWildcards: queryOptions });
|
||||
esKuery.fromKueryExpression(filterQuery.query, { allowLeadingWildcards: queryOptions });
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ export function createVegaRequestHandler({
|
|||
timeCache.setTimeRange(timeRange);
|
||||
|
||||
const esQueryConfigs = esQuery.getEsQueryConfig(uiSettings);
|
||||
const filtersDsl = esQuery.buildEsQuery(null, query, filters, esQueryConfigs);
|
||||
const filtersDsl = esQuery.buildEsQuery(undefined, query, filters, esQueryConfigs);
|
||||
const vp = new VegaParser(visParams.spec, searchCache, timeCache, filtersDsl, serviceSettings);
|
||||
|
||||
return vp.parseAsync();
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { buildEsQuery } from './build_es_query';
|
||||
import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query';
|
||||
import { fromKueryExpression, toElasticsearchQuery } from '../kuery';
|
||||
import { luceneStringToDsl } from './lucene_string_to_dsl';
|
||||
import { decorateQuery } from './decorate_query';
|
||||
import { IIndexPattern } from '../../index_patterns';
|
||||
|
|
|
@ -41,7 +41,7 @@ export interface EsQueryConfig {
|
|||
* config contains dateformat:tz
|
||||
*/
|
||||
export function buildEsQuery(
|
||||
indexPattern: IIndexPattern | null,
|
||||
indexPattern: IIndexPattern | undefined,
|
||||
queries: Query | Query[],
|
||||
filters: Filter | Filter[],
|
||||
config: EsQueryConfig = {
|
||||
|
|
|
@ -31,9 +31,8 @@ describe('filterMatchesIndex', () => {
|
|||
|
||||
it('should return true if no index pattern is passed', () => {
|
||||
const filter = { meta: { index: 'foo', key: 'bar' } } as Filter;
|
||||
const indexPattern = null;
|
||||
|
||||
expect(filterMatchesIndex(filter, indexPattern)).toBe(true);
|
||||
expect(filterMatchesIndex(filter, undefined)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if the filter key matches a field name', () => {
|
||||
|
|
|
@ -25,7 +25,7 @@ import { Filter } from '../filters';
|
|||
* this to check if `filter.meta.index` matches `indexPattern.id` instead, but that's a breaking
|
||||
* change.
|
||||
*/
|
||||
export function filterMatchesIndex(filter: Filter, indexPattern: IIndexPattern | null) {
|
||||
export function filterMatchesIndex(filter: Filter, indexPattern?: IIndexPattern | null) {
|
||||
if (!filter.meta?.key || !indexPattern) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ const translateToQuery = (filter: Filter) => {
|
|||
|
||||
export const buildQueryFromFilters = (
|
||||
filters: Filter[] = [],
|
||||
indexPattern: IIndexPattern | null,
|
||||
indexPattern: IIndexPattern | undefined,
|
||||
ignoreFilterIfFieldNotInIndex: boolean = false
|
||||
) => {
|
||||
filters = filters.filter(filter => filter && !isFilterDisabled(filter));
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { buildQueryFromKuery } from './from_kuery';
|
||||
import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query';
|
||||
import { fromKueryExpression, toElasticsearchQuery } from '../kuery';
|
||||
import { IIndexPattern } from '../../index_patterns';
|
||||
import { fields } from '../../index_patterns/mocks';
|
||||
import { Query } from '../../query/types';
|
||||
|
@ -30,7 +30,7 @@ describe('build query', () => {
|
|||
|
||||
describe('buildQueryFromKuery', () => {
|
||||
test('should return the parameters of an Elasticsearch bool query', () => {
|
||||
const result = buildQueryFromKuery(null, [], true);
|
||||
const result = buildQueryFromKuery(undefined, [], true);
|
||||
const expected = {
|
||||
must: [],
|
||||
filter: [],
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { fromKueryExpression, toElasticsearchQuery, nodeTypes, KueryNode } from '@kbn/es-query';
|
||||
import { fromKueryExpression, toElasticsearchQuery, nodeTypes, KueryNode } from '../kuery';
|
||||
import { IIndexPattern } from '../../index_patterns';
|
||||
import { Query } from '../../query/types';
|
||||
|
||||
export function buildQueryFromKuery(
|
||||
indexPattern: IIndexPattern | null,
|
||||
indexPattern: IIndexPattern | undefined,
|
||||
queries: Query[] = [],
|
||||
allowLeadingWildcards: boolean = false,
|
||||
dateFormatTZ?: string
|
||||
|
@ -35,22 +35,20 @@ export function buildQueryFromKuery(
|
|||
}
|
||||
|
||||
function buildQuery(
|
||||
indexPattern: IIndexPattern | null,
|
||||
indexPattern: IIndexPattern | undefined,
|
||||
queryASTs: KueryNode[],
|
||||
config: Record<string, any> = {}
|
||||
) {
|
||||
const compoundQueryAST: KueryNode = nodeTypes.function.buildNode('and', queryASTs);
|
||||
const kueryQuery: Record<string, any> = toElasticsearchQuery(
|
||||
compoundQueryAST,
|
||||
indexPattern,
|
||||
config
|
||||
);
|
||||
const compoundQueryAST = nodeTypes.function.buildNode('and', queryASTs);
|
||||
const kueryQuery = toElasticsearchQuery(compoundQueryAST, indexPattern, config);
|
||||
|
||||
return {
|
||||
must: [],
|
||||
filter: [],
|
||||
should: [],
|
||||
must_not: [],
|
||||
...kueryQuery.bool,
|
||||
};
|
||||
return Object.assign(
|
||||
{
|
||||
must: [],
|
||||
filter: [],
|
||||
should: [],
|
||||
must_not: [],
|
||||
},
|
||||
kueryQuery.bool
|
||||
);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ describe('migrateFilter', function() {
|
|||
} as unknown) as PhraseFilter;
|
||||
|
||||
it('should migrate match filters of type phrase', function() {
|
||||
const migratedFilter = migrateFilter(oldMatchPhraseFilter, null);
|
||||
const migratedFilter = migrateFilter(oldMatchPhraseFilter, undefined);
|
||||
|
||||
expect(isEqual(migratedFilter, newMatchPhraseFilter)).toBe(true);
|
||||
});
|
||||
|
@ -48,7 +48,7 @@ describe('migrateFilter', function() {
|
|||
it('should not modify the original filter', function() {
|
||||
const oldMatchPhraseFilterCopy = clone(oldMatchPhraseFilter, true);
|
||||
|
||||
migrateFilter(oldMatchPhraseFilter, null);
|
||||
migrateFilter(oldMatchPhraseFilter, undefined);
|
||||
|
||||
expect(isEqual(oldMatchPhraseFilter, oldMatchPhraseFilterCopy)).toBe(true);
|
||||
});
|
||||
|
@ -57,7 +57,7 @@ describe('migrateFilter', function() {
|
|||
const originalFilter = {
|
||||
match_all: {},
|
||||
} as MatchAllFilter;
|
||||
const migratedFilter = migrateFilter(originalFilter, null);
|
||||
const migratedFilter = migrateFilter(originalFilter, undefined);
|
||||
|
||||
expect(migratedFilter).toBe(originalFilter);
|
||||
expect(isEqual(migratedFilter, originalFilter)).toBe(true);
|
||||
|
|
|
@ -43,7 +43,7 @@ function isMatchPhraseFilter(filter: any): filter is DeprecatedMatchPhraseFilter
|
|||
return Boolean(fieldName && get(filter, ['match', fieldName, 'type']) === 'phrase');
|
||||
}
|
||||
|
||||
export function migrateFilter(filter: Filter, indexPattern: IIndexPattern | null) {
|
||||
export function migrateFilter(filter: Filter, indexPattern?: IIndexPattern) {
|
||||
if (isMatchPhraseFilter(filter)) {
|
||||
const fieldName = Object.keys(filter.match)[0];
|
||||
const params: Record<string, any> = get(filter, ['match', fieldName]);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
import * as esQuery from './es_query';
|
||||
import * as esFilters from './filters';
|
||||
import * as esKuery from './kuery';
|
||||
import * as utils from './utils';
|
||||
|
||||
export { esFilters, esQuery, utils };
|
||||
export { esFilters, esQuery, utils, esKuery };
|
||||
|
|
421
src/plugins/data/common/es_query/kuery/ast/ast.test.ts
Normal file
421
src/plugins/data/common/es_query/kuery/ast/ast.test.ts
Normal file
|
@ -0,0 +1,421 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
fromKueryExpression,
|
||||
fromLiteralExpression,
|
||||
toElasticsearchQuery,
|
||||
doesKueryExpressionHaveLuceneSyntaxError,
|
||||
} from './ast';
|
||||
import { nodeTypes } from '../node_types';
|
||||
import { fields } from '../../../index_patterns/mocks';
|
||||
import { IIndexPattern } from '../../../index_patterns';
|
||||
import { KueryNode } from '../types';
|
||||
|
||||
describe('kuery AST API', () => {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
describe('fromKueryExpression', () => {
|
||||
test('should return a match all "is" function for whitespace', () => {
|
||||
const expected = nodeTypes.function.buildNode('is', '*', '*');
|
||||
const actual = fromKueryExpression(' ');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should return an "is" function with a null field for single literals', () => {
|
||||
const expected = nodeTypes.function.buildNode('is', null, 'foo');
|
||||
const actual = fromKueryExpression('foo');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should ignore extraneous whitespace at the beginning and end of the query', () => {
|
||||
const expected = nodeTypes.function.buildNode('is', null, 'foo');
|
||||
const actual = fromKueryExpression(' foo ');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should not split on whitespace', () => {
|
||||
const expected = nodeTypes.function.buildNode('is', null, 'foo bar');
|
||||
const actual = fromKueryExpression('foo bar');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support "and" as a binary operator', () => {
|
||||
const expected = nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('is', null, 'foo'),
|
||||
nodeTypes.function.buildNode('is', null, 'bar'),
|
||||
]);
|
||||
const actual = fromKueryExpression('foo and bar');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support "or" as a binary operator', () => {
|
||||
const expected = nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', null, 'foo'),
|
||||
nodeTypes.function.buildNode('is', null, 'bar'),
|
||||
]);
|
||||
const actual = fromKueryExpression('foo or bar');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support negation of queries with a "not" prefix', () => {
|
||||
const expected = nodeTypes.function.buildNode(
|
||||
'not',
|
||||
nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', null, 'foo'),
|
||||
nodeTypes.function.buildNode('is', null, 'bar'),
|
||||
])
|
||||
);
|
||||
const actual = fromKueryExpression('not (foo or bar)');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('"and" should have a higher precedence than "or"', () => {
|
||||
const expected = nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', null, 'foo'),
|
||||
nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('is', null, 'bar'),
|
||||
nodeTypes.function.buildNode('is', null, 'baz'),
|
||||
]),
|
||||
nodeTypes.function.buildNode('is', null, 'qux'),
|
||||
]),
|
||||
]);
|
||||
const actual = fromKueryExpression('foo or bar and baz or qux');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support grouping to override default precedence', () => {
|
||||
const expected = nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', null, 'foo'),
|
||||
nodeTypes.function.buildNode('is', null, 'bar'),
|
||||
]),
|
||||
nodeTypes.function.buildNode('is', null, 'baz'),
|
||||
]);
|
||||
const actual = fromKueryExpression('(foo or bar) and baz');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support matching against specific fields', () => {
|
||||
const expected = nodeTypes.function.buildNode('is', 'foo', 'bar');
|
||||
const actual = fromKueryExpression('foo:bar');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should also not split on whitespace when matching specific fields', () => {
|
||||
const expected = nodeTypes.function.buildNode('is', 'foo', 'bar baz');
|
||||
const actual = fromKueryExpression('foo:bar baz');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should treat quoted values as phrases', () => {
|
||||
const expected = nodeTypes.function.buildNode('is', 'foo', 'bar baz', true);
|
||||
const actual = fromKueryExpression('foo:"bar baz"');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support a shorthand for matching multiple values against a single field', () => {
|
||||
const expected = nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', 'foo', 'bar'),
|
||||
nodeTypes.function.buildNode('is', 'foo', 'baz'),
|
||||
]);
|
||||
const actual = fromKueryExpression('foo:(bar or baz)');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support "and" and "not" operators and grouping in the shorthand as well', () => {
|
||||
const expected = nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', 'foo', 'bar'),
|
||||
nodeTypes.function.buildNode('is', 'foo', 'baz'),
|
||||
]),
|
||||
nodeTypes.function.buildNode('not', nodeTypes.function.buildNode('is', 'foo', 'qux')),
|
||||
]);
|
||||
const actual = fromKueryExpression('foo:((bar or baz) and not qux)');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support exclusive range operators', () => {
|
||||
const expected = nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('range', 'bytes', {
|
||||
gt: 1000,
|
||||
}),
|
||||
nodeTypes.function.buildNode('range', 'bytes', {
|
||||
lt: 8000,
|
||||
}),
|
||||
]);
|
||||
const actual = fromKueryExpression('bytes > 1000 and bytes < 8000');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support inclusive range operators', () => {
|
||||
const expected = nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('range', 'bytes', {
|
||||
gte: 1000,
|
||||
}),
|
||||
nodeTypes.function.buildNode('range', 'bytes', {
|
||||
lte: 8000,
|
||||
}),
|
||||
]);
|
||||
const actual = fromKueryExpression('bytes >= 1000 and bytes <= 8000');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support wildcards in field names', () => {
|
||||
const expected = nodeTypes.function.buildNode('is', 'machine*', 'osx');
|
||||
const actual = fromKueryExpression('machine*:osx');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support wildcards in values', () => {
|
||||
const expected = nodeTypes.function.buildNode('is', 'foo', 'ba*');
|
||||
const actual = fromKueryExpression('foo:ba*');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should create an exists "is" query when a field is given and "*" is the value', () => {
|
||||
const expected = nodeTypes.function.buildNode('is', 'foo', '*');
|
||||
const actual = fromKueryExpression('foo:*');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support nested queries indicated by curly braces', () => {
|
||||
const expected = nodeTypes.function.buildNode(
|
||||
'nested',
|
||||
'nestedField',
|
||||
nodeTypes.function.buildNode('is', 'childOfNested', 'foo')
|
||||
);
|
||||
const actual = fromKueryExpression('nestedField:{ childOfNested: foo }');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support nested subqueries and subqueries inside nested queries', () => {
|
||||
const expected = nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('is', 'response', '200'),
|
||||
nodeTypes.function.buildNode(
|
||||
'nested',
|
||||
'nestedField',
|
||||
nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode('is', 'childOfNested', 'foo'),
|
||||
nodeTypes.function.buildNode('is', 'childOfNested', 'bar'),
|
||||
])
|
||||
),
|
||||
]);
|
||||
const actual = fromKueryExpression(
|
||||
'response:200 and nestedField:{ childOfNested:foo or childOfNested:bar }'
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support nested sub-queries inside paren groups', () => {
|
||||
const expected = nodeTypes.function.buildNode('and', [
|
||||
nodeTypes.function.buildNode('is', 'response', '200'),
|
||||
nodeTypes.function.buildNode('or', [
|
||||
nodeTypes.function.buildNode(
|
||||
'nested',
|
||||
'nestedField',
|
||||
nodeTypes.function.buildNode('is', 'childOfNested', 'foo')
|
||||
),
|
||||
nodeTypes.function.buildNode(
|
||||
'nested',
|
||||
'nestedField',
|
||||
nodeTypes.function.buildNode('is', 'childOfNested', 'bar')
|
||||
),
|
||||
]),
|
||||
]);
|
||||
const actual = fromKueryExpression(
|
||||
'response:200 and ( nestedField:{ childOfNested:foo } or nestedField:{ childOfNested:bar } )'
|
||||
);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support nested groups inside other nested groups', () => {
|
||||
const expected = nodeTypes.function.buildNode(
|
||||
'nested',
|
||||
'nestedField',
|
||||
nodeTypes.function.buildNode(
|
||||
'nested',
|
||||
'nestedChild',
|
||||
nodeTypes.function.buildNode('is', 'doublyNestedChild', 'foo')
|
||||
)
|
||||
);
|
||||
const actual = fromKueryExpression('nestedField:{ nestedChild:{ doublyNestedChild:foo } }');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromLiteralExpression', () => {
|
||||
test('should create literal nodes for unquoted values with correct primitive types', () => {
|
||||
const stringLiteral = nodeTypes.literal.buildNode('foo');
|
||||
const booleanFalseLiteral = nodeTypes.literal.buildNode(false);
|
||||
const booleanTrueLiteral = nodeTypes.literal.buildNode(true);
|
||||
const numberLiteral = nodeTypes.literal.buildNode(42);
|
||||
|
||||
expect(fromLiteralExpression('foo')).toEqual(stringLiteral);
|
||||
expect(fromLiteralExpression('true')).toEqual(booleanTrueLiteral);
|
||||
expect(fromLiteralExpression('false')).toEqual(booleanFalseLiteral);
|
||||
expect(fromLiteralExpression('42')).toEqual(numberLiteral);
|
||||
});
|
||||
|
||||
test('should allow escaping of special characters with a backslash', () => {
|
||||
const expected = nodeTypes.literal.buildNode('\\():<>"*');
|
||||
// yo dawg
|
||||
const actual = fromLiteralExpression('\\\\\\(\\)\\:\\<\\>\\"\\*');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support double quoted strings that do not need escapes except for quotes', () => {
|
||||
const expected = nodeTypes.literal.buildNode('\\():<>"*');
|
||||
const actual = fromLiteralExpression('"\\():<>\\"*"');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support escaped backslashes inside quoted strings', () => {
|
||||
const expected = nodeTypes.literal.buildNode('\\');
|
||||
const actual = fromLiteralExpression('"\\\\"');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should detect wildcards and build wildcard AST nodes', () => {
|
||||
const expected = nodeTypes.wildcard.buildNode('foo*bar');
|
||||
const actual = fromLiteralExpression('foo*bar');
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test("should return the given node type's ES query representation", () => {
|
||||
const node = nodeTypes.function.buildNode('exists', 'response');
|
||||
const expected = nodeTypes.function.toElasticsearchQuery(node, indexPattern);
|
||||
const result = toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should return an empty "and" function for undefined nodes and unknown node types', () => {
|
||||
const expected = nodeTypes.function.toElasticsearchQuery(
|
||||
nodeTypes.function.buildNode('and', []),
|
||||
indexPattern
|
||||
);
|
||||
|
||||
expect(toElasticsearchQuery((null as unknown) as KueryNode, undefined)).toEqual(expected);
|
||||
|
||||
const noTypeNode = nodeTypes.function.buildNode('exists', 'foo');
|
||||
delete noTypeNode.type;
|
||||
expect(toElasticsearchQuery(noTypeNode)).toEqual(expected);
|
||||
|
||||
const unknownTypeNode = nodeTypes.function.buildNode('exists', 'foo');
|
||||
|
||||
// @ts-ignore
|
||||
unknownTypeNode.type = 'notValid';
|
||||
expect(toElasticsearchQuery(unknownTypeNode)).toEqual(expected);
|
||||
});
|
||||
|
||||
test("should return the given node type's ES query representation including a time zone parameter when one is provided", () => {
|
||||
const config = { dateFormatTZ: 'America/Phoenix' };
|
||||
const node = nodeTypes.function.buildNode('is', '@timestamp', '"2018-04-03T19:04:17"');
|
||||
const expected = nodeTypes.function.toElasticsearchQuery(node, indexPattern, config);
|
||||
const result = toElasticsearchQuery(node, indexPattern, config);
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('doesKueryExpressionHaveLuceneSyntaxError', () => {
|
||||
test('should return true for Lucene ranges', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('bar: [1 TO 10]');
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
test('should return false for KQL ranges', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('bar < 1');
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
test('should return true for Lucene exists', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('_exists_: bar');
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
test('should return false for KQL exists', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('bar:*');
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
test('should return true for Lucene wildcards', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('bar: ba?');
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
test('should return false for KQL wildcards', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('bar: ba*');
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
test('should return true for Lucene regex', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('bar: /ba.*/');
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
test('should return true for Lucene fuzziness', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('bar: ba~');
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
test('should return true for Lucene proximity', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('bar: "ba"~2');
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
test('should return true for Lucene boosting', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('bar: ba^2');
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
test('should return true for Lucene + operator', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('+foo: bar');
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
test('should return true for Lucene - operators', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('-foo: bar');
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
test('should return true for Lucene && operators', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('foo: bar && baz: qux');
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
test('should return true for Lucene || operators', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('foo: bar || baz: qux');
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
test('should return true for mixed KQL/Lucene queries', () => {
|
||||
const result = doesKueryExpressionHaveLuceneSyntaxError('foo: bar and (baz: qux || bag)');
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -17,21 +17,44 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { nodeTypes } from '../node_types/index';
|
||||
import { parse as parseKuery } from './kuery';
|
||||
import { KQLSyntaxError } from '../errors';
|
||||
import { KQLSyntaxError } from '../kuery_syntax_error';
|
||||
import { KueryNode, JsonObject, DslQuery, KueryParseOptions } from '../types';
|
||||
import { IIndexPattern } from '../../../index_patterns/types';
|
||||
|
||||
export function fromLiteralExpression(expression, parseOptions) {
|
||||
parseOptions = {
|
||||
...parseOptions,
|
||||
startRule: 'Literal',
|
||||
};
|
||||
// @ts-ignore
|
||||
import { parse as parseKuery } from './_generated_/kuery';
|
||||
|
||||
return fromExpression(expression, parseOptions, parseKuery);
|
||||
}
|
||||
const fromExpression = (
|
||||
expression: string | DslQuery,
|
||||
parseOptions: Partial<KueryParseOptions> = {},
|
||||
parse: Function = parseKuery
|
||||
): KueryNode => {
|
||||
if (typeof expression === 'undefined') {
|
||||
throw new Error('expression must be a string, got undefined instead');
|
||||
}
|
||||
|
||||
export function fromKueryExpression(expression, parseOptions) {
|
||||
return parse(expression, { ...parseOptions, helpers: { nodeTypes } });
|
||||
};
|
||||
|
||||
export const fromLiteralExpression = (
|
||||
expression: string | DslQuery,
|
||||
parseOptions: Partial<KueryParseOptions> = {}
|
||||
): KueryNode => {
|
||||
return fromExpression(
|
||||
expression,
|
||||
{
|
||||
...parseOptions,
|
||||
startRule: 'Literal',
|
||||
},
|
||||
parseKuery
|
||||
);
|
||||
};
|
||||
|
||||
export const fromKueryExpression = (
|
||||
expression: string | DslQuery,
|
||||
parseOptions: Partial<KueryParseOptions> = {}
|
||||
): KueryNode => {
|
||||
try {
|
||||
return fromExpression(expression, parseOptions, parseKuery);
|
||||
} catch (error) {
|
||||
|
@ -41,20 +64,18 @@ export function fromKueryExpression(expression, parseOptions) {
|
|||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function fromExpression(expression, parseOptions = {}, parse = parseKuery) {
|
||||
if (_.isUndefined(expression)) {
|
||||
throw new Error('expression must be a string, got undefined instead');
|
||||
export const doesKueryExpressionHaveLuceneSyntaxError = (
|
||||
expression: string | DslQuery
|
||||
): boolean => {
|
||||
try {
|
||||
fromExpression(expression, { errorOnLuceneSyntax: true }, parseKuery);
|
||||
return false;
|
||||
} catch (e) {
|
||||
return e.message.startsWith('Lucene');
|
||||
}
|
||||
|
||||
parseOptions = {
|
||||
...parseOptions,
|
||||
helpers: { nodeTypes },
|
||||
};
|
||||
|
||||
return parse(expression, parseOptions);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @params {String} indexPattern
|
||||
|
@ -63,19 +84,17 @@ function fromExpression(expression, parseOptions = {}, parse = parseKuery) {
|
|||
* 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, config = {}, context = {}) {
|
||||
export const toElasticsearchQuery = (
|
||||
node: KueryNode,
|
||||
indexPattern?: IIndexPattern,
|
||||
config?: Record<string, any>,
|
||||
context?: Record<string, any>
|
||||
): JsonObject => {
|
||||
if (!node || !node.type || !nodeTypes[node.type]) {
|
||||
return toElasticsearchQuery(nodeTypes.function.buildNode('and', []));
|
||||
return toElasticsearchQuery(nodeTypes.function.buildNode('and', []), indexPattern);
|
||||
}
|
||||
|
||||
return nodeTypes[node.type].toElasticsearchQuery(node, indexPattern, config, context);
|
||||
}
|
||||
const nodeType = (nodeTypes[node.type] as unknown) as any;
|
||||
|
||||
export function doesKueryExpressionHaveLuceneSyntaxError(expression) {
|
||||
try {
|
||||
fromExpression(expression, { errorOnLuceneSyntax: true }, parseKuery);
|
||||
return false;
|
||||
} catch (e) {
|
||||
return (e.message.startsWith('Lucene'));
|
||||
}
|
||||
}
|
||||
return nodeType.toElasticsearchQuery(node, indexPattern, config, context);
|
||||
};
|
|
@ -17,43 +17,53 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import * as and from '../and';
|
||||
import { nodeTypes } from '../../node_types';
|
||||
import * as ast from '../../ast';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
import { nodeTypes } from '../node_types';
|
||||
import { fields } from '../../../index_patterns/mocks';
|
||||
import { IIndexPattern } from '../../../index_patterns';
|
||||
import * as ast from '../ast';
|
||||
|
||||
let indexPattern;
|
||||
// @ts-ignore
|
||||
import * as and from './and';
|
||||
|
||||
const childNode1 = nodeTypes.function.buildNode('is', 'machine.os', 'osx');
|
||||
const childNode2 = nodeTypes.function.buildNode('is', 'extension', 'jpg');
|
||||
|
||||
describe('kuery functions', function () {
|
||||
describe('and', function () {
|
||||
describe('kuery functions', () => {
|
||||
describe('and', () => {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
describe('buildNodeParams', function () {
|
||||
|
||||
it('arguments should contain the unmodified child nodes', function () {
|
||||
describe('buildNodeParams', () => {
|
||||
test('arguments should contain the unmodified child nodes', () => {
|
||||
const result = and.buildNodeParams([childNode1, childNode2]);
|
||||
const { arguments: [ actualChildNode1, actualChildNode2 ] } = result;
|
||||
expect(actualChildNode1).to.be(childNode1);
|
||||
expect(actualChildNode2).to.be(childNode2);
|
||||
const {
|
||||
arguments: [actualChildNode1, actualChildNode2],
|
||||
} = result;
|
||||
|
||||
expect(actualChildNode1).toBe(childNode1);
|
||||
expect(actualChildNode2).toBe(childNode2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
|
||||
it('should wrap subqueries in an ES bool query\'s filter clause', function () {
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test("should wrap subqueries in an ES bool query's filter clause", () => {
|
||||
const node = nodeTypes.function.buildNode('and', [childNode1, childNode2]);
|
||||
const result = and.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.only.have.keys('bool');
|
||||
expect(result.bool).to.only.have.keys('filter');
|
||||
expect(result.bool.filter).to.eql(
|
||||
[childNode1, childNode2].map((childNode) => ast.toElasticsearchQuery(childNode, indexPattern))
|
||||
|
||||
expect(result).toHaveProperty('bool');
|
||||
expect(Object.keys(result).length).toBe(1);
|
||||
expect(result.bool).toHaveProperty('filter');
|
||||
expect(Object.keys(result.bool).length).toBe(1);
|
||||
|
||||
expect(result.bool.filter).toEqual(
|
||||
[childNode1, childNode2].map(childNode =>
|
||||
ast.toElasticsearchQuery(childNode, indexPattern)
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
|
@ -17,67 +17,73 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import * as exists from '../exists';
|
||||
import { nodeTypes } from '../../node_types';
|
||||
import _ from 'lodash';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
import { nodeTypes } from '../node_types';
|
||||
import { fields } from '../../../index_patterns/mocks';
|
||||
import { IIndexPattern } from '../../../index_patterns';
|
||||
|
||||
// @ts-ignore
|
||||
import * as exists from './exists';
|
||||
|
||||
let indexPattern;
|
||||
|
||||
describe('kuery functions', function () {
|
||||
describe('exists', function () {
|
||||
describe('kuery functions', () => {
|
||||
describe('exists', () => {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
describe('buildNodeParams', function () {
|
||||
it('should return a single "arguments" param', function () {
|
||||
describe('buildNodeParams', () => {
|
||||
test('should return a single "arguments" param', () => {
|
||||
const result = exists.buildNodeParams('response');
|
||||
expect(result).to.only.have.key('arguments');
|
||||
|
||||
expect(result).toHaveProperty('arguments');
|
||||
expect(Object.keys(result).length).toBe(1);
|
||||
});
|
||||
|
||||
it('arguments should contain the provided fieldName as a literal', function () {
|
||||
const { arguments: [ arg ] } = exists.buildNodeParams('response');
|
||||
expect(arg).to.have.property('type', 'literal');
|
||||
expect(arg).to.have.property('value', 'response');
|
||||
test('arguments should contain the provided fieldName as a literal', () => {
|
||||
const {
|
||||
arguments: [arg],
|
||||
} = exists.buildNodeParams('response');
|
||||
|
||||
expect(arg).toHaveProperty('type', 'literal');
|
||||
expect(arg).toHaveProperty('value', 'response');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
it('should return an ES exists query', function () {
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test('should return an ES exists query', () => {
|
||||
const expected = {
|
||||
exists: { field: 'response' }
|
||||
exists: { field: 'response' },
|
||||
};
|
||||
|
||||
const existsNode = nodeTypes.function.buildNode('exists', 'response');
|
||||
const result = exists.toElasticsearchQuery(existsNode, indexPattern);
|
||||
expect(_.isEqual(expected, result)).to.be(true);
|
||||
|
||||
expect(expected).toEqual(result);
|
||||
});
|
||||
|
||||
it('should return an ES exists query without an index pattern', function () {
|
||||
test('should return an ES exists query without an index pattern', () => {
|
||||
const expected = {
|
||||
exists: { field: 'response' }
|
||||
exists: { field: 'response' },
|
||||
};
|
||||
|
||||
const existsNode = nodeTypes.function.buildNode('exists', 'response');
|
||||
const result = exists.toElasticsearchQuery(existsNode);
|
||||
expect(_.isEqual(expected, result)).to.be(true);
|
||||
|
||||
expect(expected).toEqual(result);
|
||||
});
|
||||
|
||||
it('should throw an error for scripted fields', function () {
|
||||
test('should throw an error for scripted fields', () => {
|
||||
const existsNode = nodeTypes.function.buildNode('exists', 'script string');
|
||||
expect(exists.toElasticsearchQuery)
|
||||
.withArgs(existsNode, indexPattern).to.throwException(/Exists query does not support scripted fields/);
|
||||
expect(() => exists.toElasticsearchQuery(existsNode, indexPattern)).toThrowError(
|
||||
/Exists query does not support scripted fields/
|
||||
);
|
||||
});
|
||||
|
||||
it('should use a provided nested context to create a full field name', function () {
|
||||
test('should use a provided nested context to create a full field name', () => {
|
||||
const expected = {
|
||||
exists: { field: 'nestedField.response' }
|
||||
exists: { field: 'nestedField.response' },
|
||||
};
|
||||
|
||||
const existsNode = nodeTypes.function.buildNode('exists', 'response');
|
||||
const result = exists.toElasticsearchQuery(
|
||||
existsNode,
|
||||
|
@ -85,7 +91,8 @@ describe('kuery functions', function () {
|
|||
{},
|
||||
{ nested: { path: 'nestedField' } }
|
||||
);
|
||||
expect(_.isEqual(expected, result)).to.be(true);
|
||||
|
||||
expect(expected).toEqual(result);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { nodeTypes } from '../node_types';
|
||||
import { fields } from '../../../index_patterns/mocks';
|
||||
import { IIndexPattern } from '../../../index_patterns';
|
||||
|
||||
// @ts-ignore
|
||||
import * as geoBoundingBox from './geo_bounding_box';
|
||||
|
||||
const params = {
|
||||
bottomRight: {
|
||||
lat: 50.73,
|
||||
lon: -135.35,
|
||||
},
|
||||
topLeft: {
|
||||
lat: 73.12,
|
||||
lon: -174.37,
|
||||
},
|
||||
};
|
||||
|
||||
describe('kuery functions', () => {
|
||||
describe('geoBoundingBox', () => {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
describe('buildNodeParams', () => {
|
||||
test('should return an "arguments" param', () => {
|
||||
const result = geoBoundingBox.buildNodeParams('geo', params);
|
||||
|
||||
expect(result).toHaveProperty('arguments');
|
||||
expect(Object.keys(result).length).toBe(1);
|
||||
});
|
||||
|
||||
test('arguments should contain the provided fieldName as a literal', () => {
|
||||
const result = geoBoundingBox.buildNodeParams('geo', params);
|
||||
const {
|
||||
arguments: [fieldName],
|
||||
} = result;
|
||||
|
||||
expect(fieldName).toHaveProperty('type', 'literal');
|
||||
expect(fieldName).toHaveProperty('value', 'geo');
|
||||
});
|
||||
|
||||
test('arguments should contain the provided params as named arguments with "lat, lon" string values', () => {
|
||||
const result = geoBoundingBox.buildNodeParams('geo', params);
|
||||
const {
|
||||
arguments: [, ...args],
|
||||
} = result;
|
||||
|
||||
args.map((param: any) => {
|
||||
expect(param).toHaveProperty('type', 'namedArg');
|
||||
expect(['bottomRight', 'topLeft'].includes(param.name)).toBe(true);
|
||||
expect(param.value.type).toBe('literal');
|
||||
|
||||
const { lat, lon } = get(params, param.name);
|
||||
|
||||
expect(param.value.value).toBe(`${lat}, ${lon}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test('should return an ES geo_bounding_box query representing the given node', () => {
|
||||
const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params);
|
||||
const result = geoBoundingBox.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toHaveProperty('geo_bounding_box');
|
||||
expect(result.geo_bounding_box.geo).toHaveProperty('top_left', '73.12, -174.37');
|
||||
expect(result.geo_bounding_box.geo).toHaveProperty('bottom_right', '50.73, -135.35');
|
||||
});
|
||||
|
||||
test('should return an ES geo_bounding_box query without an index pattern', () => {
|
||||
const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params);
|
||||
const result = geoBoundingBox.toElasticsearchQuery(node);
|
||||
|
||||
expect(result).toHaveProperty('geo_bounding_box');
|
||||
expect(result.geo_bounding_box.geo).toHaveProperty('top_left', '73.12, -174.37');
|
||||
expect(result.geo_bounding_box.geo).toHaveProperty('bottom_right', '50.73, -135.35');
|
||||
});
|
||||
|
||||
test('should use the ignore_unmapped parameter', () => {
|
||||
const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params);
|
||||
const result = geoBoundingBox.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result.geo_bounding_box.ignore_unmapped).toBe(true);
|
||||
});
|
||||
|
||||
test('should throw an error for scripted fields', () => {
|
||||
const node = nodeTypes.function.buildNode('geoBoundingBox', 'script number', params);
|
||||
|
||||
expect(() => geoBoundingBox.toElasticsearchQuery(node, indexPattern)).toThrowError(
|
||||
/Geo bounding box query does not support scripted fields/
|
||||
);
|
||||
});
|
||||
|
||||
test('should use a provided nested context to create a full field name', () => {
|
||||
const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params);
|
||||
const result = geoBoundingBox.toElasticsearchQuery(
|
||||
node,
|
||||
indexPattern,
|
||||
{},
|
||||
{ nested: { path: 'nestedField' } }
|
||||
);
|
||||
|
||||
expect(result).toHaveProperty('geo_bounding_box');
|
||||
expect(result.geo_bounding_box['nestedField.geo']).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { nodeTypes } from '../node_types';
|
||||
import { fields } from '../../../index_patterns/mocks';
|
||||
import { IIndexPattern } from '../../../index_patterns';
|
||||
|
||||
// @ts-ignore
|
||||
import * as geoPolygon from './geo_polygon';
|
||||
|
||||
const points = [
|
||||
{
|
||||
lat: 69.77,
|
||||
lon: -171.56,
|
||||
},
|
||||
{
|
||||
lat: 50.06,
|
||||
lon: -169.1,
|
||||
},
|
||||
{
|
||||
lat: 69.16,
|
||||
lon: -125.85,
|
||||
},
|
||||
];
|
||||
|
||||
describe('kuery functions', () => {
|
||||
describe('geoPolygon', () => {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
describe('buildNodeParams', () => {
|
||||
test('should return an "arguments" param', () => {
|
||||
const result = geoPolygon.buildNodeParams('geo', points);
|
||||
|
||||
expect(result).toHaveProperty('arguments');
|
||||
expect(Object.keys(result).length).toBe(1);
|
||||
});
|
||||
|
||||
test('arguments should contain the provided fieldName as a literal', () => {
|
||||
const result = geoPolygon.buildNodeParams('geo', points);
|
||||
const {
|
||||
arguments: [fieldName],
|
||||
} = result;
|
||||
|
||||
expect(fieldName).toHaveProperty('type', 'literal');
|
||||
expect(fieldName).toHaveProperty('value', 'geo');
|
||||
});
|
||||
|
||||
test('arguments should contain the provided points literal "lat, lon" string values', () => {
|
||||
const result = geoPolygon.buildNodeParams('geo', points);
|
||||
const {
|
||||
arguments: [, ...args],
|
||||
} = result;
|
||||
|
||||
args.forEach((param: any, index: number) => {
|
||||
const expectedPoint = points[index];
|
||||
const expectedLatLon = `${expectedPoint.lat}, ${expectedPoint.lon}`;
|
||||
|
||||
expect(param).toHaveProperty('type', 'literal');
|
||||
expect(param.value).toBe(expectedLatLon);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test('should return an ES geo_polygon query representing the given node', () => {
|
||||
const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points);
|
||||
const result = geoPolygon.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toHaveProperty('geo_polygon');
|
||||
expect(result.geo_polygon.geo).toHaveProperty('points');
|
||||
|
||||
result.geo_polygon.geo.points.forEach((point: any, index: number) => {
|
||||
const expectedLatLon = `${points[index].lat}, ${points[index].lon}`;
|
||||
|
||||
expect(point).toBe(expectedLatLon);
|
||||
});
|
||||
});
|
||||
|
||||
test('should return an ES geo_polygon query without an index pattern', () => {
|
||||
const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points);
|
||||
const result = geoPolygon.toElasticsearchQuery(node);
|
||||
|
||||
expect(result).toHaveProperty('geo_polygon');
|
||||
expect(result.geo_polygon.geo).toHaveProperty('points');
|
||||
|
||||
result.geo_polygon.geo.points.forEach((point: any, index: number) => {
|
||||
const expectedLatLon = `${points[index].lat}, ${points[index].lon}`;
|
||||
|
||||
expect(point).toBe(expectedLatLon);
|
||||
});
|
||||
});
|
||||
|
||||
test('should use the ignore_unmapped parameter', () => {
|
||||
const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points);
|
||||
const result = geoPolygon.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result.geo_polygon.ignore_unmapped).toBe(true);
|
||||
});
|
||||
|
||||
test('should throw an error for scripted fields', () => {
|
||||
const node = nodeTypes.function.buildNode('geoPolygon', 'script number', points);
|
||||
expect(() => geoPolygon.toElasticsearchQuery(node, indexPattern)).toThrowError(
|
||||
/Geo polygon query does not support scripted fields/
|
||||
);
|
||||
});
|
||||
|
||||
test('should use a provided nested context to create a full field name', () => {
|
||||
const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points);
|
||||
const result = geoPolygon.toElasticsearchQuery(
|
||||
node,
|
||||
indexPattern,
|
||||
{},
|
||||
{ nested: { path: 'nestedField' } }
|
||||
);
|
||||
|
||||
expect(result).toHaveProperty('geo_polygon');
|
||||
expect(result.geo_polygon['nestedField.geo']).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -17,20 +17,22 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import * as ast from '../ast';
|
||||
import * as literal from '../node_types/literal';
|
||||
import * as wildcard from '../node_types/wildcard';
|
||||
import { getPhraseScript } from '../../utils/filters';
|
||||
import { get, isUndefined } from 'lodash';
|
||||
import { getPhraseScript } from '../../filters';
|
||||
import { getFields } from './utils/get_fields';
|
||||
import { getTimeZoneFromSettings } from '../../utils/get_time_zone_from_settings';
|
||||
import { getFullFieldNameNode } from './utils/get_full_field_name_node';
|
||||
|
||||
import * as ast from '../ast';
|
||||
|
||||
import * as literal from '../node_types/literal';
|
||||
import * as wildcard from '../node_types/wildcard';
|
||||
|
||||
export function buildNodeParams(fieldName, value, isPhrase = false) {
|
||||
if (_.isUndefined(fieldName)) {
|
||||
if (isUndefined(fieldName)) {
|
||||
throw new Error('fieldName is a required argument');
|
||||
}
|
||||
if (_.isUndefined(value)) {
|
||||
if (isUndefined(value)) {
|
||||
throw new Error('value is a required argument');
|
||||
}
|
||||
const fieldNode = typeof fieldName === 'string' ? ast.fromLiteralExpression(fieldName) : literal.buildNode(fieldName);
|
||||
|
@ -45,7 +47,7 @@ export function toElasticsearchQuery(node, indexPattern = null, config = {}, con
|
|||
const { arguments: [fieldNameArg, valueArg, isPhraseArg] } = node;
|
||||
const fullFieldNameArg = getFullFieldNameNode(fieldNameArg, indexPattern, context.nested ? context.nested.path : undefined);
|
||||
const fieldName = ast.toElasticsearchQuery(fullFieldNameArg);
|
||||
const value = !_.isUndefined(valueArg) ? ast.toElasticsearchQuery(valueArg) : valueArg;
|
||||
const value = !isUndefined(valueArg) ? ast.toElasticsearchQuery(valueArg) : valueArg;
|
||||
const type = isPhraseArg.value ? 'phrase' : 'best_fields';
|
||||
if (fullFieldNameArg.value === null) {
|
||||
if (valueArg.type === 'wildcard') {
|
||||
|
@ -94,7 +96,7 @@ export function toElasticsearchQuery(node, indexPattern = null, config = {}, con
|
|||
// users handle this themselves so we automatically add nested queries in this scenario.
|
||||
if (
|
||||
!(fullFieldNameArg.type === 'wildcard')
|
||||
|| !_.get(field, 'subType.nested')
|
||||
|| !get(field, 'subType.nested')
|
||||
|| context.nested
|
||||
) {
|
||||
return query;
|
305
src/plugins/data/common/es_query/kuery/functions/is.test.ts
Normal file
305
src/plugins/data/common/es_query/kuery/functions/is.test.ts
Normal file
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { nodeTypes } from '../node_types';
|
||||
import { fields } from '../../../index_patterns/mocks';
|
||||
|
||||
// @ts-ignore
|
||||
import * as is from './is';
|
||||
import { IIndexPattern } from '../../../index_patterns';
|
||||
|
||||
describe('kuery functions', () => {
|
||||
describe('is', () => {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
describe('buildNodeParams', () => {
|
||||
test('fieldName and value should be required arguments', () => {
|
||||
expect(() => is.buildNodeParams()).toThrowError(/fieldName is a required argument/);
|
||||
expect(() => is.buildNodeParams('foo')).toThrowError(/value is a required argument/);
|
||||
});
|
||||
|
||||
test('arguments should contain the provided fieldName and value as literals', () => {
|
||||
const {
|
||||
arguments: [fieldName, value],
|
||||
} = is.buildNodeParams('response', 200);
|
||||
|
||||
expect(fieldName).toHaveProperty('type', 'literal');
|
||||
expect(fieldName).toHaveProperty('value', 'response');
|
||||
expect(value).toHaveProperty('type', 'literal');
|
||||
expect(value).toHaveProperty('value', 200);
|
||||
});
|
||||
|
||||
test('should detect wildcards in the provided arguments', () => {
|
||||
const {
|
||||
arguments: [fieldName, value],
|
||||
} = is.buildNodeParams('machine*', 'win*');
|
||||
|
||||
expect(fieldName).toHaveProperty('type', 'wildcard');
|
||||
expect(value).toHaveProperty('type', 'wildcard');
|
||||
});
|
||||
|
||||
test('should default to a non-phrase query', () => {
|
||||
const {
|
||||
arguments: [, , isPhrase],
|
||||
} = is.buildNodeParams('response', 200);
|
||||
expect(isPhrase.value).toBe(false);
|
||||
});
|
||||
|
||||
test('should allow specification of a phrase query', () => {
|
||||
const {
|
||||
arguments: [, , isPhrase],
|
||||
} = is.buildNodeParams('response', 200, true);
|
||||
expect(isPhrase.value).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test('should return an ES match_all query when fieldName and value are both "*"', () => {
|
||||
const expected = {
|
||||
match_all: {},
|
||||
};
|
||||
const node = nodeTypes.function.buildNode('is', '*', '*');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should return an ES multi_match query using default_field when fieldName is null', () => {
|
||||
const expected = {
|
||||
multi_match: {
|
||||
query: 200,
|
||||
type: 'best_fields',
|
||||
lenient: true,
|
||||
},
|
||||
};
|
||||
const node = nodeTypes.function.buildNode('is', null, 200);
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should return an ES query_string query using default_field when fieldName is null and value contains a wildcard', () => {
|
||||
const expected = {
|
||||
query_string: {
|
||||
query: 'jpg*',
|
||||
},
|
||||
};
|
||||
const node = nodeTypes.function.buildNode('is', null, 'jpg*');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should return an ES bool query with a sub-query for each field when fieldName is "*"', () => {
|
||||
const node = nodeTypes.function.buildNode('is', '*', 200);
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toHaveProperty('bool');
|
||||
expect(result.bool.should.length).toBe(indexPattern.fields.length);
|
||||
});
|
||||
|
||||
test('should return an ES exists query when value is "*"', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [{ exists: { field: 'extension' } }],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
const node = nodeTypes.function.buildNode('is', 'extension', '*');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should return an ES match query when a concrete fieldName and value are provided', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [{ match: { extension: 'jpg' } }],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
const node = nodeTypes.function.buildNode('is', 'extension', 'jpg');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should return an ES match query when a concrete fieldName and value are provided without an index pattern', () => {
|
||||
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).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support creation of phrase queries', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [{ match_phrase: { extension: 'jpg' } }],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
const node = nodeTypes.function.buildNode('is', 'extension', 'jpg', true);
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should create a query_string query for wildcard values', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
query_string: {
|
||||
fields: ['extension'],
|
||||
query: 'jpg*',
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
const node = nodeTypes.function.buildNode('is', 'extension', 'jpg*');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support scripted fields', () => {
|
||||
const node = nodeTypes.function.buildNode('is', 'script string', 'foo');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result.bool.should[0]).toHaveProperty('script');
|
||||
});
|
||||
|
||||
test('should support date fields without a dateFormat provided', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: '2018-04-03T19:04:17',
|
||||
lte: '2018-04-03T19:04:17',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
const node = nodeTypes.function.buildNode('is', '@timestamp', '"2018-04-03T19:04:17"');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support date fields with a dateFormat provided', () => {
|
||||
const config = { dateFormatTZ: 'America/Phoenix' };
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: '2018-04-03T19:04:17',
|
||||
lte: '2018-04-03T19:04:17',
|
||||
time_zone: 'America/Phoenix',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
const node = nodeTypes.function.buildNode('is', '@timestamp', '"2018-04-03T19:04:17"');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern, config);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should use a provided nested context to create a full field name', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [{ match: { 'nestedField.extension': 'jpg' } }],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
const node = nodeTypes.function.buildNode('is', 'extension', 'jpg');
|
||||
const result = is.toElasticsearchQuery(
|
||||
node,
|
||||
indexPattern,
|
||||
{},
|
||||
{ nested: { path: 'nestedField' } }
|
||||
);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should support wildcard field names', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [{ match: { extension: 'jpg' } }],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
const node = nodeTypes.function.buildNode('is', 'ext*', 'jpg');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should automatically add a nested query when a wildcard field name covers a nested field', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
nested: {
|
||||
path: 'nestedField.nestedChild',
|
||||
query: {
|
||||
match: {
|
||||
'nestedField.nestedChild.doublyNestedChild': 'foo',
|
||||
},
|
||||
},
|
||||
score_mode: 'none',
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
const node = nodeTypes.function.buildNode('is', '*doublyNested*', 'foo');
|
||||
const result = is.toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -17,52 +17,60 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import * as nested from '../nested';
|
||||
import { nodeTypes } from '../../node_types';
|
||||
import * as ast from '../../ast';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
import { nodeTypes } from '../node_types';
|
||||
import { fields } from '../../../index_patterns/mocks';
|
||||
import { IIndexPattern } from '../../../index_patterns';
|
||||
|
||||
let indexPattern;
|
||||
import * as ast from '../ast';
|
||||
|
||||
// @ts-ignore
|
||||
import * as nested from './nested';
|
||||
|
||||
const childNode = nodeTypes.function.buildNode('is', 'child', 'foo');
|
||||
|
||||
describe('kuery functions', function () {
|
||||
describe('nested', function () {
|
||||
describe('kuery functions', () => {
|
||||
describe('nested', () => {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
describe('buildNodeParams', function () {
|
||||
|
||||
it('arguments should contain the unmodified child nodes', function () {
|
||||
describe('buildNodeParams', () => {
|
||||
test('arguments should contain the unmodified child nodes', () => {
|
||||
const result = nested.buildNodeParams('nestedField', childNode);
|
||||
const { arguments: [ resultPath, resultChildNode ] } = result;
|
||||
expect(ast.toElasticsearchQuery(resultPath)).to.be('nestedField');
|
||||
expect(resultChildNode).to.be(childNode);
|
||||
const {
|
||||
arguments: [resultPath, resultChildNode],
|
||||
} = result;
|
||||
|
||||
expect(ast.toElasticsearchQuery(resultPath)).toBe('nestedField');
|
||||
expect(resultChildNode).toBe(childNode);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
|
||||
it('should wrap subqueries in an ES nested query', function () {
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test('should wrap subqueries in an ES nested query', () => {
|
||||
const node = nodeTypes.function.buildNode('nested', 'nestedField', childNode);
|
||||
const result = nested.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.only.have.keys('nested');
|
||||
expect(result.nested.path).to.be('nestedField');
|
||||
expect(result.nested.score_mode).to.be('none');
|
||||
|
||||
expect(result).toHaveProperty('nested');
|
||||
expect(Object.keys(result).length).toBe(1);
|
||||
|
||||
expect(result.nested.path).toBe('nestedField');
|
||||
expect(result.nested.score_mode).toBe('none');
|
||||
});
|
||||
|
||||
it('should pass the nested path to subqueries so the full field name can be used', function () {
|
||||
test('should pass the nested path to subqueries so the full field name can be used', () => {
|
||||
const node = nodeTypes.function.buildNode('nested', 'nestedField', childNode);
|
||||
const result = nested.toElasticsearchQuery(node, indexPattern);
|
||||
const expectedSubQuery = ast.toElasticsearchQuery(
|
||||
nodeTypes.function.buildNode('is', 'nestedField.child', 'foo')
|
||||
);
|
||||
expect(result.nested.query).to.eql(expectedSubQuery);
|
||||
});
|
||||
|
||||
expect(result.nested.query).toEqual(expectedSubQuery);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -17,44 +17,50 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import * as not from '../not';
|
||||
import { nodeTypes } from '../../node_types';
|
||||
import * as ast from '../../ast';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
import { nodeTypes } from '../node_types';
|
||||
import { fields } from '../../../index_patterns/mocks';
|
||||
import { IIndexPattern } from '../../../index_patterns';
|
||||
|
||||
let indexPattern;
|
||||
import * as ast from '../ast';
|
||||
|
||||
// @ts-ignore
|
||||
import * as not from './not';
|
||||
|
||||
const childNode = nodeTypes.function.buildNode('is', 'extension', 'jpg');
|
||||
|
||||
describe('kuery functions', function () {
|
||||
|
||||
describe('not', function () {
|
||||
describe('kuery functions', () => {
|
||||
describe('not', () => {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
describe('buildNodeParams', function () {
|
||||
describe('buildNodeParams', () => {
|
||||
test('arguments should contain the unmodified child node', () => {
|
||||
const {
|
||||
arguments: [actualChild],
|
||||
} = not.buildNodeParams(childNode);
|
||||
|
||||
it('arguments should contain the unmodified child node', function () {
|
||||
const { arguments: [ actualChild ] } = not.buildNodeParams(childNode);
|
||||
expect(actualChild).to.be(childNode);
|
||||
expect(actualChild).toBe(childNode);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
|
||||
it('should wrap a subquery in an ES bool query\'s must_not clause', function () {
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test("should wrap a subquery in an ES bool query's must_not clause", () => {
|
||||
const node = nodeTypes.function.buildNode('not', childNode);
|
||||
const result = not.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.only.have.keys('bool');
|
||||
expect(result.bool).to.only.have.keys('must_not');
|
||||
expect(result.bool.must_not).to.eql(ast.toElasticsearchQuery(childNode, indexPattern));
|
||||
});
|
||||
|
||||
expect(result).toHaveProperty('bool');
|
||||
expect(Object.keys(result).length).toBe(1);
|
||||
|
||||
expect(result.bool).toHaveProperty('must_not');
|
||||
expect(Object.keys(result.bool).length).toBe(1);
|
||||
|
||||
expect(result.bool.must_not).toEqual(ast.toElasticsearchQuery(childNode, indexPattern));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -17,56 +17,61 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import * as or from '../or';
|
||||
import { nodeTypes } from '../../node_types';
|
||||
import * as ast from '../../ast';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
import { nodeTypes } from '../node_types';
|
||||
import { fields } from '../../../index_patterns/mocks';
|
||||
import { IIndexPattern } from '../../../index_patterns';
|
||||
|
||||
let indexPattern;
|
||||
import * as ast from '../ast';
|
||||
|
||||
// @ts-ignore
|
||||
import * as or from './or';
|
||||
|
||||
const childNode1 = nodeTypes.function.buildNode('is', 'machine.os', 'osx');
|
||||
const childNode2 = nodeTypes.function.buildNode('is', 'extension', 'jpg');
|
||||
|
||||
describe('kuery functions', function () {
|
||||
|
||||
describe('or', function () {
|
||||
describe('kuery functions', () => {
|
||||
describe('or', () => {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
|
||||
describe('buildNodeParams', function () {
|
||||
|
||||
it('arguments should contain the unmodified child nodes', function () {
|
||||
describe('buildNodeParams', () => {
|
||||
test('arguments should contain the unmodified child nodes', () => {
|
||||
const result = or.buildNodeParams([childNode1, childNode2]);
|
||||
const { arguments: [ actualChildNode1, actualChildNode2 ] } = result;
|
||||
expect(actualChildNode1).to.be(childNode1);
|
||||
expect(actualChildNode2).to.be(childNode2);
|
||||
});
|
||||
const {
|
||||
arguments: [actualChildNode1, actualChildNode2],
|
||||
} = result;
|
||||
|
||||
expect(actualChildNode1).toBe(childNode1);
|
||||
expect(actualChildNode2).toBe(childNode2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
|
||||
it('should wrap subqueries in an ES bool query\'s should clause', function () {
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test("should wrap subqueries in an ES bool query's should clause", () => {
|
||||
const node = nodeTypes.function.buildNode('or', [childNode1, childNode2]);
|
||||
const result = or.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.only.have.keys('bool');
|
||||
expect(result.bool).to.have.keys('should');
|
||||
expect(result.bool.should).to.eql(
|
||||
[childNode1, childNode2].map((childNode) => ast.toElasticsearchQuery(childNode, indexPattern))
|
||||
|
||||
expect(result).toHaveProperty('bool');
|
||||
expect(Object.keys(result).length).toBe(1);
|
||||
expect(result.bool).toHaveProperty('should');
|
||||
expect(result.bool.should).toEqual(
|
||||
[childNode1, childNode2].map(childNode =>
|
||||
ast.toElasticsearchQuery(childNode, indexPattern)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('should require one of the clauses to match', function () {
|
||||
test('should require one of the clauses to match', () => {
|
||||
const node = nodeTypes.function.buildNode('or', [childNode1, childNode2]);
|
||||
const result = or.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result.bool).to.have.property('minimum_should_match', 1);
|
||||
|
||||
expect(result.bool).toHaveProperty('minimum_should_match', 1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -20,7 +20,7 @@
|
|||
import _ from 'lodash';
|
||||
import { nodeTypes } from '../node_types';
|
||||
import * as ast from '../ast';
|
||||
import { getRangeScript } from '../../utils/filters';
|
||||
import { getRangeScript } from '../../filters';
|
||||
import { getFields } from './utils/get_fields';
|
||||
import { getTimeZoneFromSettings } from '../../utils/get_time_zone_from_settings';
|
||||
import { getFullFieldNameNode } from './utils/get_full_field_name_node';
|
|
@ -17,53 +17,57 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import * as range from '../range';
|
||||
import { nodeTypes } from '../../node_types';
|
||||
import indexPatternResponse from '../../../__fixtures__/index_pattern_response.json';
|
||||
import { get } from 'lodash';
|
||||
import { nodeTypes } from '../node_types';
|
||||
import { fields } from '../../../index_patterns/mocks';
|
||||
import { IIndexPattern } from '../../../index_patterns';
|
||||
import { RangeFilterParams } from '../../filters';
|
||||
|
||||
let indexPattern;
|
||||
|
||||
describe('kuery functions', function () {
|
||||
|
||||
describe('range', function () {
|
||||
// @ts-ignore
|
||||
import * as range from './range';
|
||||
|
||||
describe('kuery functions', () => {
|
||||
describe('range', () => {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
describe('buildNodeParams', function () {
|
||||
|
||||
it('arguments should contain the provided fieldName as a literal', function () {
|
||||
describe('buildNodeParams', () => {
|
||||
test('arguments should contain the provided fieldName as a literal', () => {
|
||||
const result = range.buildNodeParams('bytes', { gt: 1000, lt: 8000 });
|
||||
const { arguments: [fieldName] } = result;
|
||||
const {
|
||||
arguments: [fieldName],
|
||||
} = result;
|
||||
|
||||
expect(fieldName).to.have.property('type', 'literal');
|
||||
expect(fieldName).to.have.property('value', 'bytes');
|
||||
expect(fieldName).toHaveProperty('type', 'literal');
|
||||
expect(fieldName).toHaveProperty('value', 'bytes');
|
||||
});
|
||||
|
||||
it('arguments should contain the provided params as named arguments', function () {
|
||||
const givenParams = { gt: 1000, lt: 8000, format: 'epoch_millis' };
|
||||
test('arguments should contain the provided params as named arguments', () => {
|
||||
const givenParams: RangeFilterParams = { gt: 1000, lt: 8000, format: 'epoch_millis' };
|
||||
const result = range.buildNodeParams('bytes', givenParams);
|
||||
const { arguments: [, ...params] } = result;
|
||||
const {
|
||||
arguments: [, ...params],
|
||||
} = result;
|
||||
|
||||
expect(params).to.be.an('array');
|
||||
expect(params).to.not.be.empty();
|
||||
expect(Array.isArray(params)).toBeTruthy();
|
||||
expect(params.length).toBeGreaterThan(1);
|
||||
|
||||
params.map((param) => {
|
||||
expect(param).to.have.property('type', 'namedArg');
|
||||
expect(['gt', 'lt', 'format'].includes(param.name)).to.be(true);
|
||||
expect(param.value.type).to.be('literal');
|
||||
expect(param.value.value).to.be(givenParams[param.name]);
|
||||
params.map((param: any) => {
|
||||
expect(param).toHaveProperty('type', 'namedArg');
|
||||
expect(['gt', 'lt', 'format'].includes(param.name)).toBe(true);
|
||||
expect(param.value.type).toBe('literal');
|
||||
expect(param.value.value).toBe(get(givenParams, param.name));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
|
||||
it('should return an ES range query for the node\'s field and params', function () {
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test("should return an ES range query for the node's field and params", () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
|
@ -71,21 +75,21 @@ describe('kuery functions', function () {
|
|||
range: {
|
||||
bytes: {
|
||||
gt: 1000,
|
||||
lt: 8000
|
||||
}
|
||||
}
|
||||
}
|
||||
lt: 8000,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('range', 'bytes', { gt: 1000, lt: 8000 });
|
||||
const result = range.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return an ES range query without an index pattern', function () {
|
||||
test('should return an ES range query without an index pattern', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
|
@ -93,21 +97,22 @@ describe('kuery functions', function () {
|
|||
range: {
|
||||
bytes: {
|
||||
gt: 1000,
|
||||
lt: 8000
|
||||
}
|
||||
}
|
||||
}
|
||||
lt: 8000,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
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);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should support wildcard field names', function () {
|
||||
test('should support wildcard field names', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
|
@ -115,27 +120,29 @@ describe('kuery functions', function () {
|
|||
range: {
|
||||
bytes: {
|
||||
gt: 1000,
|
||||
lt: 8000
|
||||
}
|
||||
}
|
||||
}
|
||||
lt: 8000,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('range', 'byt*', { gt: 1000, lt: 8000 });
|
||||
const result = range.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should support scripted fields', function () {
|
||||
test('should support scripted fields', () => {
|
||||
const node = nodeTypes.function.buildNode('range', 'script number', { gt: 1000, lt: 8000 });
|
||||
const result = range.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result.bool.should[0]).to.have.key('script');
|
||||
|
||||
expect(result.bool.should[0]).toHaveProperty('script');
|
||||
});
|
||||
|
||||
it('should support date fields without a dateFormat provided', function () {
|
||||
test('should support date fields without a dateFormat provided', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
|
@ -144,20 +151,23 @@ describe('kuery functions', function () {
|
|||
'@timestamp': {
|
||||
gt: '2018-01-03T19:04:17',
|
||||
lt: '2018-04-03T19:04:17',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('range', '@timestamp', { gt: '2018-01-03T19:04:17', lt: '2018-04-03T19:04:17' });
|
||||
const node = nodeTypes.function.buildNode('range', '@timestamp', {
|
||||
gt: '2018-01-03T19:04:17',
|
||||
lt: '2018-04-03T19:04:17',
|
||||
});
|
||||
const result = range.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should support date fields with a dateFormat provided', function () {
|
||||
test('should support date fields with a dateFormat provided', () => {
|
||||
const config = { dateFormatTZ: 'America/Phoenix' };
|
||||
const expected = {
|
||||
bool: {
|
||||
|
@ -168,20 +178,23 @@ describe('kuery functions', function () {
|
|||
gt: '2018-01-03T19:04:17',
|
||||
lt: '2018-04-03T19:04:17',
|
||||
time_zone: 'America/Phoenix',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('range', '@timestamp', { gt: '2018-01-03T19:04:17', lt: '2018-04-03T19:04:17' });
|
||||
const node = nodeTypes.function.buildNode('range', '@timestamp', {
|
||||
gt: '2018-01-03T19:04:17',
|
||||
lt: '2018-04-03T19:04:17',
|
||||
});
|
||||
const result = range.toElasticsearchQuery(node, indexPattern, config);
|
||||
expect(result).to.eql(expected);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should use a provided nested context to create a full field name', function () {
|
||||
test('should use a provided nested context to create a full field name', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
|
@ -189,15 +202,14 @@ describe('kuery functions', function () {
|
|||
range: {
|
||||
'nestedField.bytes': {
|
||||
gt: 1000,
|
||||
lt: 8000
|
||||
}
|
||||
}
|
||||
}
|
||||
lt: 8000,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('range', 'bytes', { gt: 1000, lt: 8000 });
|
||||
const result = range.toElasticsearchQuery(
|
||||
node,
|
||||
|
@ -205,10 +217,11 @@ describe('kuery functions', function () {
|
|||
{},
|
||||
{ nested: { path: 'nestedField' } }
|
||||
);
|
||||
expect(result).to.eql(expected);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should automatically add a nested query when a wildcard field name covers a nested field', function () {
|
||||
test('should automatically add a nested query when a wildcard field name covers a nested field', () => {
|
||||
const expected = {
|
||||
bool: {
|
||||
should: [
|
||||
|
@ -219,21 +232,24 @@ describe('kuery functions', function () {
|
|||
range: {
|
||||
'nestedField.nestedChild.doublyNestedChild': {
|
||||
gt: 1000,
|
||||
lt: 8000
|
||||
}
|
||||
}
|
||||
lt: 8000,
|
||||
},
|
||||
},
|
||||
},
|
||||
score_mode: 'none'
|
||||
}
|
||||
}
|
||||
score_mode: 'none',
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
};
|
||||
|
||||
const node = nodeTypes.function.buildNode('range', '*doublyNested*', { gt: 1000, lt: 8000 });
|
||||
const node = nodeTypes.function.buildNode('range', '*doublyNested*', {
|
||||
gt: 1000,
|
||||
lt: 8000,
|
||||
});
|
||||
const result = range.toElasticsearchQuery(node, indexPattern);
|
||||
expect(result).to.eql(expected);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -17,39 +17,41 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { getFields } from '../../utils/get_fields';
|
||||
import expect from '@kbn/expect';
|
||||
import indexPatternResponse from '../../../../__fixtures__/index_pattern_response.json';
|
||||
import { fields } from '../../../../index_patterns/mocks';
|
||||
|
||||
import { nodeTypes } from '../../..';
|
||||
import { nodeTypes } from '../../index';
|
||||
import { IIndexPattern, IFieldType } from '../../../../index_patterns';
|
||||
|
||||
let indexPattern;
|
||||
|
||||
describe('getFields', function () {
|
||||
// @ts-ignore
|
||||
import { getFields } from './get_fields';
|
||||
|
||||
describe('getFields', () => {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = indexPatternResponse;
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
describe('field names without a wildcard', function () {
|
||||
|
||||
it('should return an empty array if the field does not exist in the index pattern', function () {
|
||||
describe('field names without a wildcard', () => {
|
||||
test('should return an empty array if the field does not exist in the index pattern', () => {
|
||||
const fieldNameNode = nodeTypes.literal.buildNode('nonExistentField');
|
||||
const expected = [];
|
||||
const actual = getFields(fieldNameNode, indexPattern);
|
||||
expect(actual).to.eql(expected);
|
||||
|
||||
expect(actual).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return the single matching field in an array', function () {
|
||||
test('should return the single matching field in an array', () => {
|
||||
const fieldNameNode = nodeTypes.literal.buildNode('extension');
|
||||
const results = getFields(fieldNameNode, indexPattern);
|
||||
expect(results).to.be.an('array');
|
||||
expect(results).to.have.length(1);
|
||||
expect(results[0].name).to.be('extension');
|
||||
|
||||
expect(results).toHaveLength(1);
|
||||
expect(Array.isArray(results)).toBeTruthy();
|
||||
expect(results[0].name).toBe('extension');
|
||||
});
|
||||
|
||||
it('should not match a wildcard in a literal node', function () {
|
||||
test('should not match a wildcard in a literal node', () => {
|
||||
const indexPatternWithWildField = {
|
||||
title: 'wildIndex',
|
||||
fields: [
|
||||
|
@ -61,37 +63,32 @@ describe('getFields', function () {
|
|||
|
||||
const fieldNameNode = nodeTypes.literal.buildNode('foo*');
|
||||
const results = getFields(fieldNameNode, indexPatternWithWildField);
|
||||
expect(results).to.be.an('array');
|
||||
expect(results).to.have.length(1);
|
||||
expect(results[0].name).to.be('foo*');
|
||||
|
||||
// ensure the wildcard is not actually being parsed
|
||||
const expected = [];
|
||||
expect(results).toHaveLength(1);
|
||||
expect(Array.isArray(results)).toBeTruthy();
|
||||
expect(results[0].name).toBe('foo*');
|
||||
|
||||
const actual = getFields(nodeTypes.literal.buildNode('fo*'), indexPatternWithWildField);
|
||||
expect(actual).to.eql(expected);
|
||||
expect(actual).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('field name patterns with a wildcard', function () {
|
||||
|
||||
it('should return an empty array if it does not match any fields in the index pattern', function () {
|
||||
describe('field name patterns with a wildcard', () => {
|
||||
test('should return an empty array if test does not match any fields in the index pattern', () => {
|
||||
const fieldNameNode = nodeTypes.wildcard.buildNode('nonExistent*');
|
||||
const expected = [];
|
||||
const actual = getFields(fieldNameNode, indexPattern);
|
||||
expect(actual).to.eql(expected);
|
||||
|
||||
expect(actual).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return all fields that match the pattern in an array', function () {
|
||||
test('should return all fields that match the pattern in an array', () => {
|
||||
const fieldNameNode = nodeTypes.wildcard.buildNode('machine*');
|
||||
const results = getFields(fieldNameNode, indexPattern);
|
||||
expect(results).to.be.an('array');
|
||||
expect(results).to.have.length(2);
|
||||
expect(results.find((field) => {
|
||||
return field.name === 'machine.os';
|
||||
})).to.be.ok();
|
||||
expect(results.find((field) => {
|
||||
return field.name === 'machine.os.raw';
|
||||
})).to.be.ok();
|
||||
|
||||
expect(Array.isArray(results)).toBeTruthy();
|
||||
expect(results).toHaveLength(2);
|
||||
expect(results.find((field: IFieldType) => field.name === 'machine.os')).toBeDefined();
|
||||
expect(results.find((field: IFieldType) => field.name === 'machine.os.raw')).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { nodeTypes } from '../../node_types';
|
||||
import { fields } from '../../../../index_patterns/mocks';
|
||||
import { IIndexPattern } from '../../../../index_patterns';
|
||||
|
||||
// @ts-ignore
|
||||
import { getFullFieldNameNode } from './get_full_field_name_node';
|
||||
|
||||
describe('getFullFieldNameNode', function() {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
test('should return unchanged name node if no nested path is passed in', () => {
|
||||
const nameNode = nodeTypes.literal.buildNode('notNested');
|
||||
const result = getFullFieldNameNode(nameNode, indexPattern);
|
||||
|
||||
expect(result).toEqual(nameNode);
|
||||
});
|
||||
|
||||
test('should add the nested path if test is valid according to the index pattern', () => {
|
||||
const nameNode = nodeTypes.literal.buildNode('child');
|
||||
const result = getFullFieldNameNode(nameNode, indexPattern, 'nestedField');
|
||||
|
||||
expect(result).toEqual(nodeTypes.literal.buildNode('nestedField.child'));
|
||||
});
|
||||
|
||||
test('should throw an error if a path is provided for a non-nested field', () => {
|
||||
const nameNode = nodeTypes.literal.buildNode('os');
|
||||
expect(() => getFullFieldNameNode(nameNode, indexPattern, 'machine')).toThrowError(
|
||||
/machine.os is not a nested field but is in nested group "machine" in the KQL expression/
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw an error if a nested field is not passed with a path', () => {
|
||||
const nameNode = nodeTypes.literal.buildNode('nestedField.child');
|
||||
|
||||
expect(() => getFullFieldNameNode(nameNode, indexPattern)).toThrowError(
|
||||
/nestedField.child is a nested field, but is not in a nested group in the KQL expression./
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw an error if a nested field is passed with the wrong path', () => {
|
||||
const nameNode = nodeTypes.literal.buildNode('nestedChild.doublyNestedChild');
|
||||
|
||||
expect(() => getFullFieldNameNode(nameNode, indexPattern, 'nestedField')).toThrowError(
|
||||
/Nested field nestedField.nestedChild.doublyNestedChild is being queried with the incorrect nested path. The correct path is nestedField.nestedChild/
|
||||
);
|
||||
});
|
||||
|
||||
test('should skip error checking for wildcard names', () => {
|
||||
const nameNode = nodeTypes.wildcard.buildNode('nested*');
|
||||
const result = getFullFieldNameNode(nameNode, indexPattern);
|
||||
|
||||
expect(result).toEqual(nameNode);
|
||||
});
|
||||
|
||||
test('should skip error checking if no index pattern is passed in', () => {
|
||||
const nameNode = nodeTypes.literal.buildNode('os');
|
||||
expect(() => getFullFieldNameNode(nameNode, null, 'machine')).not.toThrowError();
|
||||
|
||||
const result = getFullFieldNameNode(nameNode, null, 'machine');
|
||||
expect(result).toEqual(nodeTypes.literal.buildNode('machine.os'));
|
||||
});
|
||||
});
|
|
@ -17,6 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export * from './ast';
|
||||
export { KQLSyntaxError } from './kuery_syntax_error';
|
||||
export { nodeTypes } from './node_types';
|
||||
export * from './errors';
|
||||
export * from './ast';
|
||||
|
||||
export * from './types';
|
|
@ -17,89 +17,92 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { fromKueryExpression } from '../ast';
|
||||
|
||||
import { fromKueryExpression } from './ast';
|
||||
|
||||
describe('kql syntax errors', () => {
|
||||
|
||||
it('should throw an error for a field query missing a value', () => {
|
||||
expect(() => {
|
||||
fromKueryExpression('response:');
|
||||
}).toThrow('Expected "(", "{", value, whitespace but end of input found.\n' +
|
||||
'response:\n' +
|
||||
'---------^');
|
||||
}).toThrow(
|
||||
'Expected "(", "{", value, whitespace but end of input found.\n' +
|
||||
'response:\n' +
|
||||
'---------^'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error for an OR query missing a right side sub-query', () => {
|
||||
expect(() => {
|
||||
fromKueryExpression('response:200 or ');
|
||||
}).toThrow('Expected "(", NOT, field name, value but end of input found.\n' +
|
||||
'response:200 or \n' +
|
||||
'----------------^');
|
||||
}).toThrow(
|
||||
'Expected "(", NOT, field name, value but end of input found.\n' +
|
||||
'response:200 or \n' +
|
||||
'----------------^'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error for an OR list of values missing a right side sub-query', () => {
|
||||
expect(() => {
|
||||
fromKueryExpression('response:(200 or )');
|
||||
}).toThrow('Expected "(", NOT, value but ")" found.\n' +
|
||||
'response:(200 or )\n' +
|
||||
'-----------------^');
|
||||
}).toThrow(
|
||||
'Expected "(", NOT, value but ")" found.\n' + 'response:(200 or )\n' + '-----------------^'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error for a NOT query missing a sub-query', () => {
|
||||
expect(() => {
|
||||
fromKueryExpression('response:200 and not ');
|
||||
}).toThrow('Expected "(", field name, value but end of input found.\n' +
|
||||
'response:200 and not \n' +
|
||||
'---------------------^');
|
||||
}).toThrow(
|
||||
'Expected "(", field name, value but end of input found.\n' +
|
||||
'response:200 and not \n' +
|
||||
'---------------------^'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error for a NOT list missing a sub-query', () => {
|
||||
expect(() => {
|
||||
fromKueryExpression('response:(200 and not )');
|
||||
}).toThrow('Expected "(", value but ")" found.\n' +
|
||||
'response:(200 and not )\n' +
|
||||
'----------------------^');
|
||||
}).toThrow(
|
||||
'Expected "(", value but ")" found.\n' +
|
||||
'response:(200 and not )\n' +
|
||||
'----------------------^'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error for unbalanced quotes', () => {
|
||||
expect(() => {
|
||||
fromKueryExpression('foo:"ba ');
|
||||
}).toThrow('Expected "(", "{", value, whitespace but """ found.\n' +
|
||||
'foo:"ba \n' +
|
||||
'----^');
|
||||
}).toThrow('Expected "(", "{", value, whitespace but """ found.\n' + 'foo:"ba \n' + '----^');
|
||||
});
|
||||
|
||||
it('should throw an error for unescaped quotes in a quoted string', () => {
|
||||
expect(() => {
|
||||
fromKueryExpression('foo:"ba "r"');
|
||||
}).toThrow('Expected AND, OR, end of input, whitespace but "r" found.\n' +
|
||||
'foo:"ba "r"\n' +
|
||||
'---------^');
|
||||
}).toThrow(
|
||||
'Expected AND, OR, end of input, whitespace but "r" found.\n' + 'foo:"ba "r"\n' + '---------^'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error for unescaped special characters in literals', () => {
|
||||
expect(() => {
|
||||
fromKueryExpression('foo:ba:r');
|
||||
}).toThrow('Expected AND, OR, end of input, whitespace but ":" found.\n' +
|
||||
'foo:ba:r\n' +
|
||||
'------^');
|
||||
}).toThrow(
|
||||
'Expected AND, OR, end of input, whitespace but ":" found.\n' + 'foo:ba:r\n' + '------^'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error for range queries missing a value', () => {
|
||||
expect(() => {
|
||||
fromKueryExpression('foo > ');
|
||||
}).toThrow('Expected literal, whitespace but end of input found.\n' +
|
||||
'foo > \n' +
|
||||
'------^');
|
||||
}).toThrow('Expected literal, whitespace but end of input found.\n' + 'foo > \n' + '------^');
|
||||
});
|
||||
|
||||
it('should throw an error for range queries missing a field', () => {
|
||||
expect(() => {
|
||||
fromKueryExpression('< 1000');
|
||||
}).toThrow('Expected "(", NOT, end of input, field name, value, whitespace but "<" found.\n' +
|
||||
'< 1000\n' +
|
||||
'^');
|
||||
}).toThrow(
|
||||
'Expected "(", NOT, end of input, field name, value, whitespace but "<" found.\n' +
|
||||
'< 1000\n' +
|
||||
'^'
|
||||
);
|
||||
});
|
||||
|
||||
});
|
|
@ -20,35 +20,46 @@
|
|||
import { repeat } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const endOfInputText = i18n.translate('kbnESQuery.kql.errors.endOfInputText', {
|
||||
const endOfInputText = i18n.translate('data.common.esQuery.kql.errors.endOfInputText', {
|
||||
defaultMessage: 'end of input',
|
||||
});
|
||||
|
||||
const grammarRuleTranslations: Record<string, string> = {
|
||||
fieldName: i18n.translate('data.common.esQuery.kql.errors.fieldNameText', {
|
||||
defaultMessage: 'field name',
|
||||
}),
|
||||
value: i18n.translate('data.common.esQuery.kql.errors.valueText', {
|
||||
defaultMessage: 'value',
|
||||
}),
|
||||
literal: i18n.translate('data.common.esQuery.kql.errors.literalText', {
|
||||
defaultMessage: 'literal',
|
||||
}),
|
||||
whitespace: i18n.translate('data.common.esQuery.kql.errors.whitespaceText', {
|
||||
defaultMessage: 'whitespace',
|
||||
}),
|
||||
};
|
||||
|
||||
interface KQLSyntaxErrorData extends Error {
|
||||
found: string;
|
||||
expected: KQLSyntaxErrorExpected[];
|
||||
location: any;
|
||||
}
|
||||
|
||||
interface KQLSyntaxErrorExpected {
|
||||
description: string;
|
||||
}
|
||||
|
||||
export class KQLSyntaxError extends Error {
|
||||
shortMessage: string;
|
||||
|
||||
constructor(error, expression) {
|
||||
const grammarRuleTranslations = {
|
||||
fieldName: i18n.translate('kbnESQuery.kql.errors.fieldNameText', {
|
||||
defaultMessage: 'field name',
|
||||
}),
|
||||
value: i18n.translate('kbnESQuery.kql.errors.valueText', {
|
||||
defaultMessage: 'value',
|
||||
}),
|
||||
literal: i18n.translate('kbnESQuery.kql.errors.literalText', {
|
||||
defaultMessage: 'literal',
|
||||
}),
|
||||
whitespace: i18n.translate('kbnESQuery.kql.errors.whitespaceText', {
|
||||
defaultMessage: 'whitespace',
|
||||
}),
|
||||
};
|
||||
|
||||
const translatedExpectations = error.expected.map((expected) => {
|
||||
constructor(error: KQLSyntaxErrorData, expression: any) {
|
||||
const translatedExpectations = error.expected.map(expected => {
|
||||
return grammarRuleTranslations[expected.description] || expected.description;
|
||||
});
|
||||
|
||||
const translatedExpectationText = translatedExpectations.join(', ');
|
||||
|
||||
const message = i18n.translate('kbnESQuery.kql.errors.syntaxError', {
|
||||
const message = i18n.translate('data.common.esQuery.kql.errors.syntaxError', {
|
||||
defaultMessage: 'Expected {expectedList} but {foundInput} found.',
|
||||
values: {
|
||||
expectedList: translatedExpectationText,
|
||||
|
@ -56,11 +67,9 @@ export class KQLSyntaxError extends Error {
|
|||
},
|
||||
});
|
||||
|
||||
const fullMessage = [
|
||||
message,
|
||||
expression,
|
||||
repeat('-', error.location.start.offset) + '^',
|
||||
].join('\n');
|
||||
const fullMessage = [message, expression, repeat('-', error.location.start.offset) + '^'].join(
|
||||
'\n'
|
||||
);
|
||||
|
||||
super(fullMessage);
|
||||
this.name = 'KQLSyntaxError';
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { fields } from '../../../index_patterns/mocks';
|
||||
|
||||
import { nodeTypes } from './index';
|
||||
import { IIndexPattern } from '../../../index_patterns';
|
||||
|
||||
// @ts-ignore
|
||||
import { buildNode, buildNodeWithArgumentNodes, toElasticsearchQuery } from './function';
|
||||
// @ts-ignore
|
||||
import { toElasticsearchQuery as isFunctionToElasticsearchQuery } from '../functions/is';
|
||||
|
||||
describe('kuery node types', () => {
|
||||
describe('function', () => {
|
||||
let indexPattern: IIndexPattern;
|
||||
|
||||
beforeEach(() => {
|
||||
indexPattern = ({
|
||||
fields,
|
||||
} as unknown) as IIndexPattern;
|
||||
});
|
||||
|
||||
describe('buildNode', () => {
|
||||
test('should return a node representing the given kuery function', () => {
|
||||
const result = buildNode('is', 'extension', 'jpg');
|
||||
|
||||
expect(result).toHaveProperty('type', 'function');
|
||||
expect(result).toHaveProperty('function', 'is');
|
||||
expect(result).toHaveProperty('arguments');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildNodeWithArgumentNodes', () => {
|
||||
test('should return a function node with the given argument list untouched', () => {
|
||||
const fieldNameLiteral = nodeTypes.literal.buildNode('extension');
|
||||
const valueLiteral = nodeTypes.literal.buildNode('jpg');
|
||||
const argumentNodes = [fieldNameLiteral, valueLiteral];
|
||||
const result = buildNodeWithArgumentNodes('is', argumentNodes);
|
||||
|
||||
expect(result).toHaveProperty('type', 'function');
|
||||
expect(result).toHaveProperty('function', 'is');
|
||||
expect(result).toHaveProperty('arguments');
|
||||
expect(result.arguments).toBe(argumentNodes);
|
||||
expect(result.arguments).toEqual(argumentNodes);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test("should return the given function type's ES query representation", () => {
|
||||
const node = buildNode('is', 'extension', 'jpg');
|
||||
const expected = isFunctionToElasticsearchQuery(node, indexPattern);
|
||||
const result = toElasticsearchQuery(node, indexPattern);
|
||||
|
||||
expect(expected).toEqual(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -21,7 +21,8 @@
|
|||
* WARNING: these typings are incomplete
|
||||
*/
|
||||
|
||||
import { JsonObject, JsonValue } from '..';
|
||||
import { IIndexPattern } from '../../../index_patterns';
|
||||
import { KueryNode, JsonValue } from '..';
|
||||
|
||||
type FunctionName =
|
||||
| 'is'
|
||||
|
@ -34,6 +35,17 @@ type FunctionName =
|
|||
| 'geoPolygon'
|
||||
| 'nested';
|
||||
|
||||
interface FunctionType {
|
||||
buildNode: (functionName: FunctionName, ...args: any[]) => FunctionTypeBuildNode;
|
||||
buildNodeWithArgumentNodes: (functionName: FunctionName, ...args: any[]) => FunctionTypeBuildNode;
|
||||
toElasticsearchQuery: (
|
||||
node: any,
|
||||
indexPattern?: IIndexPattern,
|
||||
config?: Record<string, any>,
|
||||
context?: Record<string, any>
|
||||
) => JsonValue;
|
||||
}
|
||||
|
||||
interface FunctionTypeBuildNode {
|
||||
type: 'function';
|
||||
function: FunctionName;
|
||||
|
@ -41,32 +53,40 @@ interface FunctionTypeBuildNode {
|
|||
arguments: any[];
|
||||
}
|
||||
|
||||
interface FunctionType {
|
||||
buildNode: (functionName: FunctionName, ...args: any[]) => FunctionTypeBuildNode;
|
||||
buildNodeWithArgumentNodes: (functionName: FunctionName, ...args: any[]) => FunctionTypeBuildNode;
|
||||
toElasticsearchQuery: (node: any, indexPattern: any, config: JsonObject) => JsonValue;
|
||||
}
|
||||
|
||||
interface LiteralType {
|
||||
buildNode: (
|
||||
value: null | boolean | number | string
|
||||
) => { type: 'literal'; value: null | boolean | number | string };
|
||||
buildNode: (value: null | boolean | number | string) => LiteralTypeBuildNode;
|
||||
toElasticsearchQuery: (node: any) => null | boolean | number | string;
|
||||
}
|
||||
|
||||
interface LiteralTypeBuildNode {
|
||||
type: 'literal';
|
||||
value: null | boolean | number | string;
|
||||
}
|
||||
|
||||
interface NamedArgType {
|
||||
buildNode: (name: string, value: any) => { type: 'namedArg'; name: string; value: any };
|
||||
buildNode: (name: string, value: any) => NamedArgTypeBuildNode;
|
||||
toElasticsearchQuery: (node: any) => string;
|
||||
}
|
||||
|
||||
interface NamedArgTypeBuildNode {
|
||||
type: 'namedArg';
|
||||
name: string;
|
||||
value: any;
|
||||
}
|
||||
|
||||
interface WildcardType {
|
||||
buildNode: (value: string) => { type: 'wildcard'; value: string };
|
||||
buildNode: (value: string) => WildcardTypeBuildNode;
|
||||
test: (node: any, string: string) => boolean;
|
||||
toElasticsearchQuery: (node: any) => string;
|
||||
toQueryStringQuery: (node: any) => string;
|
||||
hasLeadingWildcard: (node: any) => boolean;
|
||||
}
|
||||
|
||||
interface WildcardTypeBuildNode {
|
||||
type: 'wildcard';
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface NodeTypes {
|
||||
function: FunctionType;
|
||||
literal: LiteralType;
|
|
@ -17,34 +17,27 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import * as literal from '../literal';
|
||||
// @ts-ignore
|
||||
import { buildNode, toElasticsearchQuery } from './literal';
|
||||
|
||||
describe('kuery node types', function () {
|
||||
describe('kuery node types', () => {
|
||||
describe('literal', () => {
|
||||
describe('buildNode', () => {
|
||||
test('should return a node representing the given value', () => {
|
||||
const result = buildNode('foo');
|
||||
|
||||
describe('literal', function () {
|
||||
|
||||
describe('buildNode', function () {
|
||||
|
||||
it('should return a node representing the given value', function () {
|
||||
const result = literal.buildNode('foo');
|
||||
expect(result).to.have.property('type', 'literal');
|
||||
expect(result).to.have.property('value', 'foo');
|
||||
expect(result).toHaveProperty('type', 'literal');
|
||||
expect(result).toHaveProperty('value', 'foo');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', function () {
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test('should return the literal value represented by the given node', () => {
|
||||
const node = buildNode('foo');
|
||||
const result = toElasticsearchQuery(node);
|
||||
|
||||
it('should return the literal value represented by the given node', function () {
|
||||
const node = literal.buildNode('foo');
|
||||
const result = literal.toElasticsearchQuery(node);
|
||||
expect(result).to.be('foo');
|
||||
expect(result).toBe('foo');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { nodeTypes } from './index';
|
||||
|
||||
// @ts-ignore
|
||||
import { buildNode, toElasticsearchQuery } from './named_arg';
|
||||
|
||||
describe('kuery node types', () => {
|
||||
describe('named arg', () => {
|
||||
describe('buildNode', () => {
|
||||
test('should return a node representing a named argument with the given value', () => {
|
||||
const result = buildNode('fieldName', 'foo');
|
||||
expect(result).toHaveProperty('type', 'namedArg');
|
||||
expect(result).toHaveProperty('name', 'fieldName');
|
||||
expect(result).toHaveProperty('value');
|
||||
|
||||
const literalValue = result.value;
|
||||
expect(literalValue).toHaveProperty('type', 'literal');
|
||||
expect(literalValue).toHaveProperty('value', 'foo');
|
||||
});
|
||||
|
||||
test('should support literal nodes as values', () => {
|
||||
const value = nodeTypes.literal.buildNode('foo');
|
||||
const result = buildNode('fieldName', value);
|
||||
|
||||
expect(result.value).toBe(value);
|
||||
expect(result.value).toEqual(value);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test('should return the argument value represented by the given node', () => {
|
||||
const node = buildNode('fieldName', 'foo');
|
||||
const result = toElasticsearchQuery(node);
|
||||
|
||||
expect(result).toBe('foo');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
buildNode,
|
||||
wildcardSymbol,
|
||||
hasLeadingWildcard,
|
||||
toElasticsearchQuery,
|
||||
test as testNode,
|
||||
toQueryStringQuery,
|
||||
// @ts-ignore
|
||||
} from './wildcard';
|
||||
|
||||
describe('kuery node types', () => {
|
||||
describe('wildcard', () => {
|
||||
describe('buildNode', () => {
|
||||
test('should accept a string argument representing a wildcard string', () => {
|
||||
const wildcardValue = `foo${wildcardSymbol}bar`;
|
||||
const result = buildNode(wildcardValue);
|
||||
|
||||
expect(result).toHaveProperty('type', 'wildcard');
|
||||
expect(result).toHaveProperty('value', wildcardValue);
|
||||
});
|
||||
|
||||
test('should accept and parse a wildcard string', () => {
|
||||
const result = buildNode('foo*bar');
|
||||
|
||||
expect(result).toHaveProperty('type', 'wildcard');
|
||||
expect(result.value).toBe(`foo${wildcardSymbol}bar`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toElasticsearchQuery', () => {
|
||||
test('should return the string representation of the wildcard literal', () => {
|
||||
const node = buildNode('foo*bar');
|
||||
const result = toElasticsearchQuery(node);
|
||||
|
||||
expect(result).toBe('foo*bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toQueryStringQuery', () => {
|
||||
test('should return the string representation of the wildcard literal', () => {
|
||||
const node = buildNode('foo*bar');
|
||||
const result = toQueryStringQuery(node);
|
||||
|
||||
expect(result).toBe('foo*bar');
|
||||
});
|
||||
|
||||
test('should escape query_string query special characters other than wildcard', () => {
|
||||
const node = buildNode('+foo*bar');
|
||||
const result = toQueryStringQuery(node);
|
||||
|
||||
expect(result).toBe('\\+foo*bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('test', () => {
|
||||
test('should return a boolean indicating whether the string matches the given wildcard node', () => {
|
||||
const node = buildNode('foo*bar');
|
||||
|
||||
expect(testNode(node, 'foobar')).toBe(true);
|
||||
expect(testNode(node, 'foobazbar')).toBe(true);
|
||||
expect(testNode(node, 'foobar')).toBe(true);
|
||||
expect(testNode(node, 'fooqux')).toBe(false);
|
||||
expect(testNode(node, 'bazbar')).toBe(false);
|
||||
});
|
||||
|
||||
test('should return a true even when the string has newlines or tabs', () => {
|
||||
const node = buildNode('foo*bar');
|
||||
|
||||
expect(testNode(node, 'foo\nbar')).toBe(true);
|
||||
expect(testNode(node, 'foo\tbar')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasLeadingWildcard', () => {
|
||||
test('should determine whether a wildcard node contains a leading wildcard', () => {
|
||||
const node = buildNode('foo*bar');
|
||||
expect(hasLeadingWildcard(node)).toBe(false);
|
||||
|
||||
const leadingWildcardNode = buildNode('*foobar');
|
||||
expect(hasLeadingWildcard(leadingWildcardNode)).toBe(true);
|
||||
});
|
||||
|
||||
// Lone wildcards become exists queries, so we aren't worried about their performance
|
||||
test('should not consider a lone wildcard to be a leading wildcard', () => {
|
||||
const leadingWildcardNode = buildNode('*');
|
||||
|
||||
expect(hasLeadingWildcard(leadingWildcardNode)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -17,14 +17,29 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export * from './ast';
|
||||
import { NodeTypes } from './node_types';
|
||||
|
||||
export interface KueryNode {
|
||||
type: keyof NodeTypes;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export type DslQuery = any;
|
||||
|
||||
export interface KueryParseOptions {
|
||||
helpers: {
|
||||
[key: string]: any;
|
||||
};
|
||||
startRule: string;
|
||||
allowLeadingWildcards: boolean;
|
||||
errorOnLuceneSyntax: boolean;
|
||||
}
|
||||
|
||||
export { nodeTypes } from './node_types';
|
||||
|
||||
export type JsonArray = JsonValue[];
|
||||
export type JsonValue = null | boolean | number | string | JsonObject | JsonArray;
|
||||
|
||||
export interface JsonObject {
|
||||
[key: string]: JsonValue;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface JsonArray extends Array<JsonValue> {}
|
|
@ -17,10 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { FieldFormat } from '../field_format';
|
||||
import { FieldFormat, IFieldFormatType } from '../field_format';
|
||||
import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types';
|
||||
|
||||
export const createCustomFieldFormat = (convert: TextContextTypeConvert) =>
|
||||
export const createCustomFieldFormat = (convert: TextContextTypeConvert): IFieldFormatType =>
|
||||
class CustomFieldFormat extends FieldFormat {
|
||||
static id = FIELD_FORMAT_IDS.CUSTOM;
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ export abstract class FieldFormat {
|
|||
*/
|
||||
public type: any = this.constructor;
|
||||
|
||||
private readonly _params: any;
|
||||
protected readonly _params: any;
|
||||
protected getConfig: Function | undefined;
|
||||
|
||||
constructor(_params: any = {}, getConfig?: Function) {
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
module.exports = {
|
||||
kuery: {
|
||||
src: 'packages/kbn-es-query/src/kuery/ast/kuery.peg',
|
||||
dest: 'packages/kbn-es-query/src/kuery/ast/kuery.js',
|
||||
src: 'src/plugins/data/common/es_query/kuery/ast/kuery.peg',
|
||||
dest: 'src/plugins/data/common/es_query/kuery/ast/_generated_/kuery.js',
|
||||
options: {
|
||||
allowedStartRules: ['start', 'Literal']
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
import React, { useState } from 'react';
|
||||
import { uniqueId, startsWith } from 'lodash';
|
||||
import styled from 'styled-components';
|
||||
import { StaticIndexPattern } from 'ui/index_patterns';
|
||||
import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { fromQuery, toQuery } from '../Links/url_helpers';
|
||||
// @ts-ignore
|
||||
|
@ -17,12 +15,14 @@ import { getBoolFilter } from './get_bool_filter';
|
|||
import { useLocation } from '../../../hooks/useLocation';
|
||||
import { useUrlParams } from '../../../hooks/useUrlParams';
|
||||
import { history } from '../../../utils/history';
|
||||
import {
|
||||
AutocompleteSuggestion,
|
||||
AutocompleteProvider
|
||||
} from '../../../../../../../../src/plugins/data/public';
|
||||
import { useDynamicIndexPattern } from '../../../hooks/useDynamicIndexPattern';
|
||||
import { usePlugins } from '../../../new-platform/plugin';
|
||||
import { useDynamicIndexPattern } from '../../../hooks/useDynamicIndexPattern';
|
||||
import {
|
||||
AutocompleteProvider,
|
||||
AutocompleteSuggestion,
|
||||
esKuery,
|
||||
IIndexPattern
|
||||
} from '../../../../../../../../src/plugins/data/public';
|
||||
|
||||
const Container = styled.div`
|
||||
margin-bottom: 10px;
|
||||
|
@ -33,18 +33,15 @@ interface State {
|
|||
isLoadingSuggestions: boolean;
|
||||
}
|
||||
|
||||
function convertKueryToEsQuery(
|
||||
kuery: string,
|
||||
indexPattern: StaticIndexPattern
|
||||
) {
|
||||
const ast = fromKueryExpression(kuery);
|
||||
return toElasticsearchQuery(ast, indexPattern);
|
||||
function convertKueryToEsQuery(kuery: string, indexPattern: IIndexPattern) {
|
||||
const ast = esKuery.fromKueryExpression(kuery);
|
||||
return esKuery.toElasticsearchQuery(ast, indexPattern);
|
||||
}
|
||||
|
||||
function getSuggestions(
|
||||
query: string,
|
||||
selectionStart: number,
|
||||
indexPattern: StaticIndexPattern,
|
||||
indexPattern: IIndexPattern,
|
||||
boolFilter: unknown,
|
||||
autocompleteProvider?: AutocompleteProvider
|
||||
) {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { toElasticsearchQuery, fromKueryExpression } from '@kbn/es-query';
|
||||
import { ESFilter } from '../../../../typings/elasticsearch';
|
||||
import { UIFilters } from '../../../../typings/ui-filters';
|
||||
import { getEnvironmentUiFilterES } from './get_environment_ui_filter_es';
|
||||
|
@ -12,10 +11,13 @@ import {
|
|||
localUIFilters,
|
||||
localUIFilterNames
|
||||
} from '../../ui_filters/local_ui_filters/config';
|
||||
import { StaticIndexPattern } from '../../../../../../../../src/legacy/core_plugins/data/public';
|
||||
import {
|
||||
esKuery,
|
||||
IIndexPattern
|
||||
} from '../../../../../../../../src/plugins/data/server';
|
||||
|
||||
export function getUiFiltersES(
|
||||
indexPattern: StaticIndexPattern | undefined,
|
||||
indexPattern: IIndexPattern | undefined,
|
||||
uiFilters: UIFilters
|
||||
) {
|
||||
const { kuery, environment, ...localFilterValues } = uiFilters;
|
||||
|
@ -43,13 +45,13 @@ export function getUiFiltersES(
|
|||
}
|
||||
|
||||
function getKueryUiFilterES(
|
||||
indexPattern: StaticIndexPattern | undefined,
|
||||
indexPattern: IIndexPattern | undefined,
|
||||
kuery?: string
|
||||
) {
|
||||
if (!kuery || !indexPattern) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ast = fromKueryExpression(kuery);
|
||||
return toElasticsearchQuery(ast, indexPattern) as ESFilter;
|
||||
const ast = esKuery.fromKueryExpression(kuery);
|
||||
return esKuery.toElasticsearchQuery(ast, indexPattern) as ESFilter;
|
||||
}
|
||||
|
|
|
@ -4,11 +4,10 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { npStart } from 'ui/new_platform';
|
||||
import { ElasticsearchAdapter } from './adapter_types';
|
||||
import { AutocompleteSuggestion } from '../../../../../../../../src/plugins/data/public';
|
||||
import { AutocompleteSuggestion, esKuery } from '../../../../../../../../src/plugins/data/public';
|
||||
import { setup as data } from '../../../../../../../../src/legacy/core_plugins/data/public/legacy';
|
||||
|
||||
const getAutocompleteProvider = (language: string) =>
|
||||
|
@ -20,7 +19,7 @@ export class RestElasticsearchAdapter implements ElasticsearchAdapter {
|
|||
|
||||
public isKueryValid(kuery: string): boolean {
|
||||
try {
|
||||
fromKueryExpression(kuery);
|
||||
esKuery.fromKueryExpression(kuery);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
|
@ -31,9 +30,9 @@ export class RestElasticsearchAdapter implements ElasticsearchAdapter {
|
|||
if (!this.isKueryValid(kuery)) {
|
||||
return '';
|
||||
}
|
||||
const ast = fromKueryExpression(kuery);
|
||||
const ast = esKuery.fromKueryExpression(kuery);
|
||||
const indexPattern = await this.getIndexPattern();
|
||||
return JSON.stringify(toElasticsearchQuery(ast, indexPattern));
|
||||
return JSON.stringify(esKuery.toElasticsearchQuery(ast, indexPattern));
|
||||
}
|
||||
public async getSuggestions(
|
||||
kuery: string,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue