'use strict' import { test } from 'tap' /* eslint-disable no-unused-vars */ import * as types from '../../dsl/lib/types' /* eslint-enable no-unused-vars */ import { Q } from '../../dsl' test('Q is a function that creates the final query object', t => { t.type(Q, 'function') t.deepEqual(Q(c('a'), c('b'), c('c')), { query: { bool: { must: [ c('a'), c('b'), c('c') ] } } }) const topLevelKeys = [ 'aggs', 'collapse', 'explain', 'from', 'highlight', 'indices_boost', 'min_score', 'post_filter', 'profile', 'rescore', 'script_fields', 'search_after', 'size', 'slice', 'sort', '_source', 'suggest', 'terminate_after', 'timeout', 'track_scores', 'version' ] const randomTopLevelKey = topLevelKeys[Math.floor(Math.random() * topLevelKeys.length)] t.deepEqual(Q({ match: { foo: 'bar' } }, { match: { foo: 'baz' } }, { [randomTopLevelKey]: 42 }), { query: { bool: { must: [ { match: { foo: 'bar' } }, { match: { foo: 'baz' } } ] } }, [randomTopLevelKey]: 42 }) t.deepEqual(Q({ match: { foo: 'bar' } }, { [randomTopLevelKey]: 42 }), { query: { match: { foo: 'bar' } }, [randomTopLevelKey]: 42 }) t.deepEqual(Q({ query: { function_score: { max_boost: 42 } } }, { [randomTopLevelKey]: 42 }), { query: { function_score: { max_boost: 42 } }, [randomTopLevelKey]: 42 }) t.deepEqual(Q({ query: { bool: {} } }, { [randomTopLevelKey]: 42 }), { query: { bool: {} }, [randomTopLevelKey]: 42 }) t.deepEqual(Q({ sort: ['foo'] }, { sort: ['bar'] }), { sort: ['foo', 'bar'] }) t.end() }) test('Compile a query (safe)', t => { t.type(Q.param, 'function') t.type(Q.compile, 'function') const query = Q( Q.match('a', Q.param('a')), Q.match('b', Q.param('b')), Q.match('c', Q.param('c')) ) interface Input { a: number, b: number, c: number } const compiledQuery = Q.compile(query) t.deepEqual(compiledQuery({ a: 1, b: 2, c: 3 }), { query: { bool: { must: [ { match: { a: 1 } }, { match: { b: 2 } }, { match: { c: 3 } } ] } } }) try { Q.compile(Q.bool()) } catch (err) { t.is(err.message, 'The query does not contain any use of `Q.params`') } t.end() }) test('Compile a query (unsafe)', t => { t.type(Q.param, 'function') t.type(Q.compile, 'function') const query = Q( Q.match('a', Q.param('a')), Q.match('b', Q.param('b')), Q.match('c', Q.param('c')) ) interface Input { a: number, b: number, c: number } const compiledQuery = Q.compileUnsafe(query) t.deepEqual(compiledQuery({ a: 1, b: 2, c: 3 }), { query: { bool: { must: [ { match: { a: 1 } }, { match: { b: 2 } }, { match: { c: 3 } } ] } } }) try { Q.compileUnsafe(Q.bool()) } catch (err) { t.is(err.message, 'The query does not contain any use of `Q.params`') } const badQuery = Q( Q.match('a', Q.param('a')), Q.match('b', Q.param('b')), Q.match('c', Q.param('c')), // @ts-expect-error { ['__proto__']: 42 }, { constructor: { prototype: 42 } } ) const compiledBadQuery = Q.compileUnsafe(badQuery) t.deepEqual(compiledBadQuery({ a: 1, b: 2, c: 3 }), { query: { bool: { must: [ { match: { a: 1 } }, { match: { b: 2 } }, { match: { c: 3 } }, {} ] } } }) t.end() }) test('match returns a match query', t => { t.type(Q.match, 'function') t.test('simple query', t => { t.deepEqual(Q.match('foo', 'bar'), { match: { foo: 'bar' } }) t.end() }) t.test('simple query', t => { t.deepEqual(Q.match('foo', ['bar', 'baz']), [ { match: { foo: 'bar' } }, { match: { foo: 'baz' } } ]) t.end() }) t.test('complex query', t => { t.deepEqual(Q.match('foo', 'bar', { analyzer: 'and' }), { match: { foo: { query: 'bar', analyzer: 'and' } } }) t.end() }) t.end() }) test('matchPhrase returns a match_phrase query', t => { t.type(Q.matchPhrase, 'function') t.test('simple query', t => { t.deepEqual(Q.matchPhrase('foo', 'bar'), { match_phrase: { foo: 'bar' } }) t.end() }) t.test('complex query', t => { t.deepEqual(Q.matchPhrase('foo', 'bar', { analyzer: 'test' }), { match_phrase: { foo: { query: 'bar', analyzer: 'test' } } }) t.end() }) t.end() }) test('matchPhrasePrefix returns a match_phrase_prefix query', t => { t.type(Q.matchPhrasePrefix, 'function') t.test('simple query', t => { t.deepEqual(Q.matchPhrasePrefix('foo', 'bar'), { match_phrase_prefix: { foo: 'bar' } }) t.end() }) t.test('complex query', t => { t.deepEqual(Q.matchPhrasePrefix('foo', 'bar', { max_expansions: 10 }), { match_phrase_prefix: { foo: { query: 'bar', max_expansions: 10 } } }) t.end() }) t.end() }) test('multiMatch returns a multi_match query', t => { t.type(Q.multiMatch, 'function') t.deepEqual(Q.multiMatch(['foo', 'baz'], 'bar', { analyzer: 'best_fields' }), { multi_match: { query: 'bar', fields: ['foo', 'baz'], analyzer: 'best_fields' } }) t.end() }) test('matchAll returns a match_all query', t => { t.type(Q.matchAll, 'function') t.deepEqual(Q.matchAll({ norm_field: 'foobar' }), { match_all: { norm_field: 'foobar' } }) t.end() }) test('matchNone returns a match_none query', t => { t.type(Q.matchNone, 'function') t.deepEqual(Q.matchNone(), { match_none: {} }) t.end() }) test('common returns a common query', t => { t.type(Q.common, 'function') t.deepEqual(Q.common('foo', 'bar', { cutoff_frequency: 0.001 }), { common: { foo: { query: 'bar', cutoff_frequency: 0.001 } } }) t.end() }) test('queryString returns a query_string query', t => { t.type(Q.queryString, 'function') t.deepEqual(Q.queryString('foo', { default_field: 'content' }), { query_string: { query: 'foo', default_field: 'content' } }) t.end() }) test('simpleQueryString returns a simple_query_string query', t => { t.type(Q.simpleQueryString, 'function') t.deepEqual(Q.simpleQueryString('foo', { analyzer: 'content' }), { simple_query_string: { query: 'foo', analyzer: 'content' } }) t.end() }) test('term returns a term query', t => { t.type(Q.term, 'function') t.test('simple query', t => { t.deepEqual(Q.term('foo', 'bar'), { term: { foo: 'bar' } }) t.end() }) t.test('complex query', t => { t.deepEqual(Q.term('foo', 'bar', { boost: 2.0 }), { term: { foo: { value: 'bar', boost: 2.0 } } }) t.end() }) t.end() }) test('terms returns a terms query', t => { t.type(Q.terms, 'function') t.deepEqual(Q.terms('foo', ['bar', 'baz']), { terms: { foo: ['bar', 'baz'] } }) t.deepEqual( Q.terms('foo', ['bar', 'baz']), Q.term('foo', ['bar', 'baz']) ) t.end() }) test('termsSet returns a terms_set query', t => { t.type(Q.termsSet, 'function') t.deepEqual(Q.termsSet('foo', ['bar', 'baz'], { minimum_should_match_field: 'required_matches' }), { terms_set: { foo: { terms: ['bar', 'baz'], minimum_should_match_field: 'required_matches' } } }) t.end() }) test('range returns a range query', t => { t.type(Q.range, 'function') t.deepEqual(Q.range('foo', { gte: 2 }), { range: { foo: { gte: 2 } } }) t.end() }) test('exists returns a exists query', t => { t.type(Q.exists, 'function') t.deepEqual(Q.exists('foo'), { exists: { field: 'foo' } }) t.deepEqual(Q.exists(['foo', 'bar']), [{ exists: { field: 'foo' } }, { exists: { field: 'bar' } }]) t.end() }) test('prefix returns a prefix query', t => { t.type(Q.prefix, 'function') t.test('simple query', t => { t.deepEqual(Q.prefix('foo', 'bar'), { prefix: { foo: 'bar' } }) t.end() }) t.test('complex query', t => { t.deepEqual(Q.prefix('foo', 'bar', { boost: 2.0 }), { prefix: { foo: { value: 'bar', boost: 2.0 } } }) t.end() }) t.end() }) test('wildcard returns a wildcard query', t => { t.type(Q.wildcard, 'function') t.test('simple query', t => { t.deepEqual(Q.wildcard('foo', 'bar'), { wildcard: { foo: 'bar' } }) t.end() }) t.test('complex query', t => { t.deepEqual(Q.wildcard('foo', 'bar', { boost: 2.0 }), { wildcard: { foo: { value: 'bar', boost: 2.0 } } }) t.end() }) t.end() }) test('regexp returns a regexp query', t => { t.type(Q.regexp, 'function') t.test('simple query', t => { t.deepEqual(Q.regexp('foo', 'bar'), { regexp: { foo: 'bar' } }) t.end() }) t.test('complex query', t => { t.deepEqual(Q.regexp('foo', 'bar', { boost: 2.0 }), { regexp: { foo: { value: 'bar', boost: 2.0 } } }) t.end() }) t.end() }) test('fuzzy returns a fuzzy query', t => { t.type(Q.fuzzy, 'function') t.test('simple query', t => { t.deepEqual(Q.fuzzy('foo', 'bar'), { fuzzy: { foo: 'bar' } }) t.end() }) t.test('complex query', t => { t.deepEqual(Q.fuzzy('foo', 'bar', { boost: 2.0 }), { fuzzy: { foo: { value: 'bar', boost: 2.0 } } }) t.end() }) t.end() }) test('ids returns a ids query', t => { t.type(Q.ids, 'function') t.deepEqual(Q.ids('foo', ['bar', 'baz']), { ids: { foo: { values: ['bar', 'baz'] } } }) t.end() }) test('must returns a must block', t => { t.type(Q.must, 'function') t.deepEqual(Q.must(c('foo'), c('bar'), c('baz')), { must: [c('foo'), c('bar'), c('baz')] }) // should flat arrays t.deepEqual(Q.must(c('foo'), [c('bar')], c('baz')), { must: [c('foo'), c('bar'), c('baz')] }) t.deepEqual( Q.must( c('foo'), Q.must(c('bar')), Q.bool(Q.must(c('baz'))) ), { must: [c('foo'), c('bar'), c('baz')] } ) t.deepEqual( Q.must( c('foo'), Q.must(c('bar'), Q.bool(Q.must(c('faz')))), Q.bool(Q.must(c('baz'))) ), { must: [c('foo'), c('bar'), c('faz'), c('baz')] } ) t.deepEqual( Q.must( c('foo'), Q.must(c('bar')), Q.bool(Q.should(c('baz'))) ), { must: [c('foo'), c('bar'), { bool: Q.should(c('baz')) }] } ) t.deepEqual( Q.must( c('foo'), Q.must(c('bar')), Q.should(c('baz')) ), { must: [c('foo'), c('bar'), { bool: Q.should(c('baz')) }] } ) t.end() }) test('should returns a should block', t => { t.type(Q.should, 'function') t.deepEqual(Q.should(c('foo'), c('bar'), c('baz')), { should: [c('foo'), c('bar'), c('baz')] }) // should flat arrays t.deepEqual(Q.should(c('foo'), [c('bar')], c('baz')), { should: [c('foo'), c('bar'), c('baz')] }) t.deepEqual( Q.should( c('foo'), Q.should(c('bar')), Q.bool(Q.should(c('baz'))) ), { should: [c('foo'), c('bar'), c('baz')] } ) t.deepEqual( Q.should( c('foo'), Q.should(c('bar'), Q.bool(Q.should(c('faz')))), Q.bool(Q.should(c('baz'))) ), { should: [c('foo'), c('bar'), c('faz'), c('baz')] } ) t.deepEqual( Q.should( c('foo'), Q.should(c('bar')), Q.bool(Q.must(c('baz'))) ), { should: [c('foo'), c('bar'), { bool: Q.must(c('baz')) }] } ) t.deepEqual( Q.should( c('foo'), Q.should(c('bar')), Q.must(c('baz')) ), { should: [c('foo'), c('bar'), { bool: Q.must(c('baz')) }] } ) t.deepEqual( Q.should( c('foo'), Q.should(c('bar')), Q.bool(Q.should(c('baz'), c('faz')), { minimum_should_match: 1 }) ), { should: [ c('foo'), c('bar'), { bool: { ...Q.should(c('baz'), c('faz')), minimum_should_match: 1 } } ] } ) t.deepEqual( Q.should( c('foo'), Q.should(c('bar')), { ...Q.should(c('baz'), c('faz')), minimum_should_match: 1 } ), { should: [ c('foo'), c('bar'), { bool: { ...Q.should(c('baz'), c('faz')), minimum_should_match: 1 } } ] } ) t.deepEqual( Q.should( c('foo'), { ...Q.should(c('bar1'), c('bar2')), minimum_should_match: 1 }, Q.bool(Q.should(c('baz1'), c('baz2')), { minimum_should_match: 1 }) ), { should: [ c('foo'), { bool: { ...Q.should(c('bar1'), c('bar2')), minimum_should_match: 1 } }, { bool: { ...Q.should(c('baz1'), c('baz2')), minimum_should_match: 1 } } ] } ) t.end() }) test('mustNot returns a must_not block', t => { t.type(Q.mustNot, 'function') t.deepEqual(Q.mustNot(c('foo'), c('bar'), c('baz')), { must_not: [c('foo'), c('bar'), c('baz')] }) // should flat arrays t.deepEqual(Q.mustNot(c('foo'), [c('bar')], c('baz')), { must_not: [c('foo'), c('bar'), c('baz')] }) t.deepEqual( Q.mustNot( c('foo'), Q.mustNot(c('bar')), Q.bool(Q.mustNot(c('baz'))) ), { must_not: [c('foo'), c('bar'), c('baz')] } ) t.deepEqual( Q.mustNot( c('foo'), Q.mustNot(c('bar'), Q.bool(Q.mustNot(c('faz')))), Q.bool(Q.mustNot(c('baz'))) ), { must_not: [c('foo'), c('bar'), c('faz'), c('baz')] } ) t.deepEqual( Q.mustNot( c('foo'), Q.mustNot(c('bar')), Q.bool(Q.should(c('baz'))) ), { must_not: [c('foo'), c('bar'), { bool: Q.should(c('baz')) }] } ) t.deepEqual( Q.mustNot( c('foo'), Q.mustNot(c('bar')), Q.should(c('baz')) ), { must_not: [c('foo'), c('bar'), { bool: Q.should(c('baz')) }] } ) t.end() }) test('filter returns a filter block', t => { t.type(Q.filter, 'function') t.deepEqual(Q.filter(c('foo'), c('bar'), c('baz')), { filter: [c('foo'), c('bar'), c('baz')] }) // should flat arrays t.deepEqual(Q.filter(c('foo'), [c('bar')], c('baz')), { filter: [c('foo'), c('bar'), c('baz')] }) t.deepEqual( Q.filter( c('foo'), Q.filter(c('bar')), Q.bool(Q.filter(c('baz'))) ), { filter: [c('foo'), c('bar'), c('baz')] } ) t.deepEqual( Q.filter( c('foo'), Q.filter(c('bar'), Q.bool(Q.filter(c('faz')))), Q.bool(Q.filter(c('baz'))) ), { filter: [c('foo'), c('bar'), c('faz'), c('baz')] } ) t.deepEqual( Q.filter( c('foo'), Q.filter(c('bar')), Q.bool(Q.should(c('baz'))) ), { filter: [c('foo'), c('bar'), { bool: Q.should(c('baz')) }] } ) t.deepEqual( Q.filter( c('foo'), Q.filter(c('bar')), Q.should(c('baz')) ), { filter: [c('foo'), c('bar'), { bool: Q.should(c('baz')) }] } ) t.end() }) test('bool returns a bool query block', t => { t.type(Q.bool, 'function') t.deepEqual(Q.bool(Q.must(c('foo')), Q.should(c('bar')), Q.filter(c('baz'))), { bool: { must: [c('foo')], should: [c('bar')], filter: [c('baz')] } }) t.deepEqual(Q.bool(), { bool: {} }) t.deepEqual(Q.bool(undefined), { bool: {} }) t.deepEqual(Q.bool(Q.must(c('foo')), Q.should(c('bar')), Q.mustNot(c('foz')), Q.filter(c('baz'), c('faz'))), { bool: { must: [c('foo')], must_not: [c('foz')], should: [c('bar')], filter: [c('baz'), c('faz')] } }) t.deepEqual(Q.bool( c('foo'), Q.bool( Q.name('name'), Q.must(c('faz')) ), Q.should(c('bar')), Q.filter(c('baz')) ), { bool: { must: [ c('foo'), { bool: { must: [c('faz')], _name: 'name' } } ], should: [c('bar')], filter: [c('baz')] } }) t.deepEqual(Q.bool( Q.must(Q.term('hello', 'world')), Q.match('foo', 'bar'), Q.match('baz', 'faz'), Q.minShouldMatch(1) ), { bool: { must: [ { term: { hello: 'world' } } ], should: [ { match: { foo: 'bar' } }, { match: { baz: 'faz' } } ], minimum_should_match: 1 } }) t.deepEqual(Q.bool( Q.must(Q.term('hello', 'world')), Q.match('foo', 'bar'), Q.match('baz', 'faz'), Q.minShouldMatch(2) ), { bool: { must: [ { term: { hello: 'world' } }, { match: { foo: 'bar' } }, { match: { baz: 'faz' } } ] } }) t.deepEqual(Q.bool( Q.match('foo', 'bar'), Q.match('baz', 'faz'), Q.minShouldMatch(2) ), { bool: { must: [ { match: { foo: 'bar' } }, { match: { baz: 'faz' } } ] } }) t.deepEqual(Q.bool( Q.match('foo', ['bar', 'baz']) ), { bool: { must: [ { match: { foo: 'bar' } }, { match: { foo: 'baz' } } ] } }) t.end() }) test('nested returns a nested query block', t => { t.type(Q.nested, 'function') const query = Q.bool(Q.must(c('foo')), Q.should(c('bar')), Q.filter(c('baz'))) t.deepEqual(Q.nested('foo', query, { ignore_unmapped: true }), { nested: { path: 'foo', ignore_unmapped: true, ...query } }) t.end() }) test('constantScore returns a constant_score query block', t => { t.type(Q.constantScore, 'function') const query = Q.bool(Q.must(c('foo')), Q.should(c('bar')), Q.filter(c('baz'))) t.deepEqual(Q.constantScore(query, 2.0), { constant_score: { boost: 2.0, filter: query } }) t.end() }) test('disMax returns a dis_max query block', t => { t.type(Q.disMax, 'function') t.test('simple query', t => { t.deepEqual(Q.disMax([c('a'), c('b'), c('c')]), { dis_max: { queries: [c('a'), c('b'), c('c')] } }) t.end() }) t.test('complex query', t => { t.deepEqual(Q.disMax([c('a'), c('b'), c('c')], { tie_breaker: 1.0, boost: 1.0 }), { dis_max: { tie_breaker: 1.0, boost: 1.0, queries: [c('a'), c('b'), c('c')] } }) t.end() }) t.end() }) test('functionScore returns a function_score query block', t => { t.type(Q.functionScore, 'function') t.deepEqual(Q.functionScore({ max_boost: 42 }), { function_score: { max_boost: 42 } }) t.end() }) test('boosting returns a boosting query block', t => { t.type(Q.boosting, 'function') t.deepEqual(Q.boosting({ negative_boost: 42 }), { boosting: { negative_boost: 42 } }) t.end() }) test('sort returns a sort block', t => { t.type(Q.sort, 'function') t.deepEqual(Q.sort('foo'), { sort: ['foo'] }) t.deepEqual(Q.sort(['foo', 'bar']), { sort: ['foo', 'bar'] }) t.deepEqual(Q.sort('foo', 'desc'), { sort: [{ foo: 'desc' }] }) t.deepEqual(Q.sort(['foo', 'bar'], 'desc'), { sort: [{ foo: 'desc' }, { bar: 'desc' }] }) t.deepEqual(Q.sort('foo', { order: 'desc' }), { sort: [{ foo: { order: 'desc' } }] }) t.deepEqual(Q.sort(['foo', 'bar'], { order: 'desc' }), { sort: [{ foo: { order: 'desc' } }, { bar: { order: 'desc' } }] }) t.end() }) test('size', t => { t.type(Q.size, 'function') t.deepEqual(Q.size(5), { size: 5 }) t.end() }) test('minShouldMatch', t => { t.type(Q.minShouldMatch, 'function') t.deepEqual(Q.minShouldMatch(5), { minimum_should_match: 5 }) t.end() }) test('name', t => { t.type(Q.name, 'function') t.deepEqual(Q.name('test'), { _name: 'test' }) t.end() }) test('script', t => { t.type(Q.script, 'function') t.deepEqual( Q.script("doc['num1'].value > 1"), { script: "doc['num1'].value > 1" } ) t.deepEqual( Q.script("doc['num1'].value > 1", 'painless'), { script: { source: "doc['num1'].value > 1", lang: 'painless' } } ) t.deepEqual( Q.script("doc['num1'].value > 1", { foo: 'bar' }), { script: { source: "doc['num1'].value > 1", params: { foo: 'bar' } } } ) t.deepEqual( Q.script("doc['num1'].value > 1", { foo: 'bar' }, 'painless'), { script: { source: "doc['num1'].value > 1", params: { foo: 'bar' }, lang: 'painless' } } ) t.end() }) // build a condition bloc function c (key: string): types.Condition { return { match: { [key]: key } } }