import { isString , isEmpty , isPlainObject , TSError , isWildCardString , debugLogger , toString , primitiveToString , isPrimitiveValue , toBoolean , toNumber , toIntegerOrThrow , toFloatOrThrow , getTypeOf , isBooleanLike , isNotNil } from '@terascope/utils' ; import { Netmask } from 'netmask' ; import { xLuceneFieldType , xLuceneVariables , CoordinateTuple , Maybe } from '@terascope/types' ; import * as i from './interfaces' ; export const logger = debugLogger ( 'xlucene-parser' ) ; function _getType ( node ) { if ( ! node || typeof node !== 'object' ) return ; return ( node as any ) . type || undefined ; } export function isLogicalGroup ( node ) { return _getType ( node ) === i . NodeType . LogicalGroup ; } export function isConjunction ( node ) { return _getType ( node ) === i . NodeType . Conjunction ; } export function isNegation ( node ) { return _getType ( node ) === i . NodeType . Negation ; } export function isFieldGroup ( node ) { return _getType ( node ) === i . NodeType . FieldGroup ; } export function isExists ( node ) { return _getType ( node ) === i . NodeType . Exists ; } export function isRange ( node ) { return _getType ( node ) === i . NodeType . Range ; } export function isFunctionNode ( node ) { return _getType ( node ) === i . NodeType . Function ; } export function isRegexp ( node ) { return _getType ( node ) === i . NodeType . Regexp ; } export function isWildcard ( node ) { return _getType ( node ) === i . NodeType . Wildcard ; } export function isWildcardField ( node ) { return ! ! ( node && isWildCardString ( ( node as any ) . field ) ) ; } export function isTerm ( node ) { return _getType ( node ) === i . NodeType . Term ; } export function isTermList ( node ) { return _getType ( node ) === i . NodeType . TermList ; } export function isEmptyNode ( node ) { return isEmpty ( node ) || ( _getType ( node ) === i . NodeType . Empty ) ; } export function isStringDataType ( node ) { return ! ! ( node && ( node as any ) . field_type === 'string' ) ; } export const numberDataTypes = [ xLuceneFieldType . Integer , xLuceneFieldType . Float ] ; export function isNumberDataType ( node ) { return ! ! ( node && numberDataTypes . includes ( ( node as any ) . field_type ) ) ; } export function isBooleanDataType ( node ) { return ! ! ( node && ( node as any ) . field_type === 'boolean' ) ; } export function getField ( node ) { if ( ! node ) return ; if ( ! ( node as any ) . field || ! isString ( ( node as any ) . field ) ) return ; return ( node as any ) . field ; } /** term level queries with field (string|null)  */ export const termTypes = [ i . NodeType . Term , i . NodeType . Regexp , i . NodeType . Range , i . NodeType . Wildcard , i . NodeType . Function , i . NodeType . TermList , ] ; export function isTermType ( node ) { return ! ! ( node && termTypes . includes ( ( node as any ) . type ) ) ; } /** logical group or field group with flow */ export const groupTypes = [ i . NodeType . LogicalGroup , i . NodeType . FieldGroup ] ; export function isGroupLike ( node ) { return ! ! ( node && groupTypes . includes ( ( node as any ) . type ) ) ; } export function validateVariables ( obj ) { if ( ! isPlainObject ( obj ) ) { throw new TSError ( 'Invalid xLuceneVariables configuration provided, it must be an object' , { context : { safe : true } , statusCode : 400 } ) ; } return { ... obj } ; } export function getFieldValue < T > ( value , variables , allowNil ) ; export function getFieldValue < T > ( value , variables , allowNil ? ) ; export function getFieldValue < T > ( value , variables , allowNil ? ) ; export function getFieldValue < T > ( value , variables , allowNil ? ) { if ( Array . isArray ( value ) ) { return value . map ( ( val ) => getFieldValue < any > ( val , variables , true ) ) . filter ( isNotNil ) ; } if ( value . type === 'variable' ) { if ( variables [ value . value ] != null ) { return variables [ value . value ] as T ; } if ( allowNil ) return null ; if ( value . scoped ) { throw new Error ( `Could not find a scoped variable ${ value . value }` ) ; } else { throw new Error ( `Could not find a variable $${ value . value }` ) ; } } if ( value . value != null ) { return value . value ; } if ( allowNil ) return null ; throw new Error ( `Missing value in xLucene query, got ${ toString ( value ) }` ) ; } export function isInfiniteValue ( input ? ) { return input === '*' || input === Number . NEGATIVE_INFINITY || input === Number . POSITIVE_INFINITY ; } export function isInfiniteMin ( min ? ) { if ( min == null ) return false ; return min === '*' || min === Number . NEGATIVE_INFINITY ; } export function isInfiniteMax ( max ? ) { if ( max == null ) return false ; return max === '*' || max === Number . POSITIVE_INFINITY ; } export interface ParsedRange { 'gte' ? : number | string ; 'gt' ? : number | string ; 'lte' ? : number | string ; 'lt' ? : number | string ; } export function parseRange ( node , variables , excludeInfinite = false ) { const results = { } ; const leftValue = getFieldValue ( node . left . value , variables ) ; if ( ! excludeInfinite || ! isInfiniteValue ( leftValue ) ) { results [ node . left . operator ] = leftValue ; } if ( node . right ) { const rightValue = getFieldValue ( node . right . value , variables ) ; if ( ! excludeInfinite || ! isInfiniteValue ( rightValue ) ) { results [ node . right . operator ] = rightValue ; } } return results ; } export function coordinateToXlucene ( cord ) { return `"${ cord [ 1 ] }, ${ cord [ 0 ] }"` ; } type CoerceValueFns = Partial < Record < xLuceneFieldType , ( ( value : any ) => any ) > > ; export const coerceValueFns = Object . freeze ( { [ xLuceneFieldType . AnalyzedString ] : primitiveToString , [ xLuceneFieldType . String ] : primitiveToString , [ xLuceneFieldType . Boolean ] ( value ) { if ( ! isBooleanLike ( value ) ) { throw new Error ( `Expected ${ value } (${ getTypeOf ( value ) }) to be in a boolean like format` ) ; } return toBoolean ( value ) ; } , [ xLuceneFieldType . Date ] ( value ) { if ( ! isPrimitiveValue ( value ) ) { throw new Error ( `Expected ${ value } (${ getTypeOf ( value ) }) to be in a date like format` ) ; } return value ; } , [ xLuceneFieldType . Number ] ( value ) { const numValue = toNumber ( value ) ; if ( Number . isNaN ( numValue ) ) { throw new Error ( `Expected ${ value } (${ getTypeOf ( value ) }) to be in a number like format` ) ; } return numValue ; } , [ xLuceneFieldType . Float ] : toFloatOrThrow , [ xLuceneFieldType . Integer ] : toIntegerOrThrow , [ xLuceneFieldType . IP ] ( value ) { if ( typeof value !== 'string' ) { throw new Error ( `Expected ${ value } (${ getTypeOf ( value ) }) to be in a IP like format` ) ; } return value ; } , } ) ; export function makeCoerceFn ( fieldType ) { if ( ! fieldType || ! ( fieldType in coerceValueFns ) ) return ( v ) => v ; const coerceFn = coerceValueFns [ fieldType ] ! ; return coerceFn ; } export function createIPRangeFromTerm ( node , value ) { const { start , end } = parseIPRange ( value ) ; return { type : i . NodeType . Range , field : node . field , field_type : node . field_type , left : { operator : 'gte' , field_type : xLuceneFieldType . IP , value : { type : 'value' , value : start , } } , right : { operator : 'lte' , field_type : xLuceneFieldType . IP , value : { type : 'value' , value : end , } , } } ; } function parseIPRange ( val ) { try { const block = new Netmask ( val ) ; const end = block . broadcast ? block . broadcast : block . last ; return { start : block . base , end } ; } catch ( err ) { throw new Error ( `Invalid value ${ val }, could not convert to ip_range` ) ; } }