import { contains , equals , includes } from ".." ; import { CoatyObject } from "./object" ; import { ContextFilter , ObjectFilterCondition , ObjectFilterOperator , ObjectFilterProperties } from "./object-filter" ; /**
 * Provides a static `matchesFilter` method to match an object against a
 * given object filter. Useful for retrieving matching objects on Query events
 * without using a database adapter. Also useful to filter out Coaty objects
 * that match given filter conditions before publishing with Advertise or
 * Channel events.
 */ export class ObjectMatcher { private static COLLATOR : Intl . Collator ; /**
     * Gets an Intl.Collator instance that is shared by all callers.
     * To be used to efficiently perform language-sensitive string comparison.
     * The returned collator object uses default locales and options.
     */ static get collator ( ) { let collator = ObjectMatcher . COLLATOR ; if ( ! collator ) { collator = ObjectMatcher . COLLATOR = new Intl . Collator ( ) ; } return collator ; } param obj param filter returns static matchesFilter ( obj , filter ) { if ( obj === undefined ) { return false ; } if ( filter === undefined ) { return true ; } if ( filter . conditions === undefined ) { return true ; } if ( Array . isArray ( filter . conditions ) ) { return ObjectMatcher . _matchesCondition ( obj , filter . conditions ) ; } if ( filter . conditions . and ) { return filter . conditions . and . every ( cond => ObjectMatcher . _matchesCondition ( obj , cond ) ) ; } if ( filter . conditions . or ) { return filter . conditions . or . some ( cond => ObjectMatcher . _matchesCondition ( obj , cond ) ) ; } return true ; } internal param propNames returns static getFilterProperties ( propNames ) { if ( typeof propNames === "string" ) { return propNames . split ( "." ) ; } return propNames ; } internal param propNames param obj returns static getFilterPropertyValue ( propNames , obj ) { return ObjectMatcher . getFilterProperties ( propNames ) . reduce ( ( p , c ) => p !== null && typeof p === "object" ? p [ c ] : undefined , obj ) ; } private static _matchesCondition ( obj , condition ) { const [ props , [ op , value1 , value2 ] ] = condition ; const value = ObjectMatcher . getFilterPropertyValue ( props , obj ) ; if ( op === ObjectFilterOperator . NotExists ) { return value === undefined ; } if ( value === undefined ) { return false ; } switch ( op ) { case ObjectFilterOperator . LessThan : if ( typeof value === "string" && typeof value1 === "string" ) { return ObjectMatcher . collator . compare ( value , value1 ) < 0 ; } return value < value1 ; case ObjectFilterOperator . LessThanOrEqual : if ( typeof value === "string" && typeof value1 === "string" ) { return ObjectMatcher . collator . compare ( value , value1 ) <= 0 ; } return value <= value1 ; case ObjectFilterOperator . GreaterThan : if ( typeof value === "string" && typeof value1 === "string" ) { return ObjectMatcher . collator . compare ( value , value1 ) > 0 ; } return value > value1 ; case ObjectFilterOperator . GreaterThanOrEqual : if ( typeof value === "string" && typeof value1 === "string" ) { return ObjectMatcher . collator . compare ( value , value1 ) >= 0 ; } return value >= value1 ; case ObjectFilterOperator . Between : if ( typeof value === "string" && typeof value1 === "string" && typeof value2 === "string" ) { if ( ObjectMatcher . collator . compare ( value1 , value2 ) > 0 ) { return ObjectMatcher . collator . compare ( value , value2 ) >= 0 && ObjectMatcher . collator . compare ( value , value1 ) <= 0 ; } return ObjectMatcher . collator . compare ( value , value1 ) >= 0 && ObjectMatcher . collator . compare ( value , value2 ) <= 0 ; } if ( value1 > value2 ) { return value >= value2 && value <= value1 ; } return value >= value1 && value <= value2 ; case ObjectFilterOperator . NotBetween : if ( typeof value === "string" && typeof value1 === "string" && typeof value2 === "string" ) { if ( ObjectMatcher . collator . compare ( value1 , value2 ) > 0 ) { return ObjectMatcher . collator . compare ( value , value2 ) < 0 || ObjectMatcher . collator . compare ( value , value1 ) > 0 ; } return ObjectMatcher . collator . compare ( value , value1 ) < 0 || ObjectMatcher . collator . compare ( value , value2 ) > 0 ; } if ( value1 > value2 ) { return value < value2 || value > value1 ; } return value < value1 || value > value2 ; case ObjectFilterOperator . Like : if ( typeof value !== "string" || typeof value1 !== "string" ) { return false ; } const cachedRegex = condition [ 1 ] [ "likeRegex" ] ; const likeRegex = cachedRegex || ObjectMatcher . _createLikeRegexp ( value1 ) ; if ( ! cachedRegex ) { condition [ 1 ] [ "likeRegex" ] = likeRegex ; } return likeRegex . test ( value ) ; case ObjectFilterOperator . Equals : return equals ( value , value1 ) ; case ObjectFilterOperator . NotEquals : return ! equals ( value , value1 ) ; case ObjectFilterOperator . Exists : return true ; case ObjectFilterOperator . Contains : return contains ( value , value1 ) ; case ObjectFilterOperator . NotContains : return ! contains ( value , value1 ) ; case ObjectFilterOperator . In : return includes ( value1 , value ) ; case ObjectFilterOperator . NotIn : return ! includes ( value1 , value ) ; default : return false ; } } private static _createLikeRegexp ( pattern ) { let regexStr = "^" ; let isEscaped = false ; for ( const c of pattern ) { if ( c === "\\" ) { if ( isEscaped ) { isEscaped = false ; regexStr += "\\\\" ; } else { isEscaped = true ; } continue ; } if ( ".*+?^${}()|[]" . indexOf ( c ) !== - 1 ) { regexStr += "\\" + c ; isEscaped = false ; continue ; } if ( c === "_" && ! isEscaped ) { regexStr += "." ; continue ; } if ( c === "%" && ! isEscaped ) { regexStr += ".*" ; continue ; } regexStr += c ; isEscaped = false ; } regexStr += "$" ; return new RegExp ( regexStr ) ; } }