import * as RegExpUtil from "../core/regExpUtil" ; import { QueryDocument } from "./queryBuilder" ; import { MappingModel } from "../mapping/mappingModel" ; import { ArrayMapping } from "../mapping/arrayMapping" ; import { InternalMapping } from "../mapping/internalMapping" ; import { EntityMapping } from "../mapping/entityMapping" ; import { WriteContext } from "../mapping/writeContext" ; import { PersistenceError } from "../persistenceError" ; hidden export class CriteriaBuilder { /**
     * The last error encounter by the CriteriaBuilder. The value is cleared before each build.
     */ error : Error ; constructor ( protected mapping ) { } param criteria build ( criteria ) { this . error = undefined ; var preparedCriteria = this . prepareQueryDocument ( criteria , this . mapping ) ; if ( ! preparedCriteria ) { return null ; } this . mapping . setQueryDocumentDiscriminator ( preparedCriteria ) ; return preparedCriteria ; } protected prepareQueryDocument ( query , mapping ? , withinField ? ) { if ( ! query ) return { } ; var result = { } ; for ( var key in query ) { if ( query . hasOwnProperty ( key ) ) { var value = query [ key ] , preparedKey = key , preparedValue = value ; if ( key [ 0 ] == "$" ) { if ( key == "$and" || key == "$or" || key == "$nor" ) { if ( ! Array . isArray ( value ) ) { this . error = new PersistenceError ( "Value of '" + key + "' operator should be an array." ) ; return null ; } var arr = new Array ( value . length ) ; for ( var i = 0 , l = value . length ; i < l ; i ++ ) { arr [ i ] = this . prepareQueryDocument ( value [ i ] , mapping ) ; } preparedValue = arr ; } else if ( key == "$where" || key == "$text" ) { if ( withinField ) { this . error = new PersistenceError ( "Operator '" + key + "' is not allowed in $elemMatch." ) ; return null ; } preparedValue = value ; } else if ( withinField ) { return this . _prepareQueryExpression ( key , query , mapping ) ; } else { this . error = new PersistenceError ( "Unknown top-level operator '" + key + "'." ) ; return null ; } } else { if ( key == "_id" ) { if ( typeof value === "string" ) { preparedValue = ( < EntityMapping > this . mapping . inheritanceRoot ) . identity . fromString ( value ) ; } if ( value == null || preparedValue == null ) { this . error = new PersistenceError ( "Missing or invalid identifier for '_id'." ) ; return null ; } } else { var context = mapping . resolve ( key ) ; if ( context . error ) { this . error = context . error ; return null ; } var resolvedMapping = context . resolvedMapping ; if ( this . isQueryExpression ( value ) ) { if ( resolvedMapping . flags & MappingModel . MappingFlags . Array ) { resolvedMapping = ( < ArrayMapping > resolvedMapping ) . elementMapping ; } preparedValue = this . _prepareQueryExpression ( key , value , resolvedMapping ) ; } else { if ( resolvedMapping . flags & MappingModel . MappingFlags . Array ) { var arrayMapping = ( < ArrayMapping > resolvedMapping ) ; if ( ! Array . isArray ( value ) || ( arrayMapping . nestedDepth > 1 && arrayMapping . nestedDepth > this . _findArrayDepth ( value ) ) ) { resolvedMapping = arrayMapping . elementMapping ; } } preparedValue = this . prepareQueryValue ( context . resolvedPath , value , resolvedMapping ) ; } preparedKey = context . resolvedPath ; } } ( result || ( result = { } ) ) [ preparedKey ] = preparedValue ; } } return result ; } private _prepareQueryExpression ( operator , query , mapping ) { if ( ! query ) { this . error = new PersistenceError ( "Missing value for operator '" + operator + "'." ) ; return null ; } var result = { } ; for ( var key in query ) { if ( query . hasOwnProperty ( key ) ) { if ( key [ 0 ] != "$" ) { this . error = new PersistenceError ( "Unexpected value '" + key + "' in query expression." ) ; return null ; } var value = query [ key ] , preparedValue ; switch ( key ) { case '$gt' : case '$gte' : case '$lt' : case '$lte' : case '$ne' : preparedValue = this . prepareQueryValue ( key , value , mapping ) ; break ; case '$in' : case '$nin' : case '$all' : preparedValue = this . prepareArrayOfValues ( key , value , mapping ) ; break ; case '$not' : preparedValue = this . _prepareQueryExpression ( key , value , mapping ) ; break ; case '$elemMatch' : preparedValue = this . prepareQueryDocument ( value , mapping , true ) ; break ; case '$exists' : case '$type' : case '$mod' : case '$regex' : case '$options' : case '$geoIntersects' : case '$geoWithin' : case '$nearSphere' : case '$near' : case '$size' : case '$comment' : preparedValue = value ; break ; default : this . error = new PersistenceError ( "Unknown query operator '" + key + "'." ) ; return null ; } result [ key ] = preparedValue ; } } return result ; } protected prepareArrayOfValues ( operator , value , mapping ) { if ( ! Array . isArray ( value ) ) { this . error = new PersistenceError ( "Expected array for '" + operator + "' operator." ) ; return null ; } var result = new Array ( value . length ) ; for ( var i = 0 , l = value . length ; i < l ; i ++ ) { result [ i ] = this . prepareQueryValue ( operator , value [ i ] , mapping ) ; } return result ; } protected prepareQueryValue ( path , value , mapping ) { if ( ( mapping . flags & MappingModel . MappingFlags . String ) && ( value instanceof RegExp ) ) { return RegExpUtil . clone ( value ) ; } var context = new WriteContext ( path ) ; var preparedValue = mapping . write ( context , value ) ; if ( context . hasErrors ) { this . error = new PersistenceError ( `Bad value: ${ context . getErrorMessage ( ) }` ) ; return null ; } return preparedValue ; } param value param depth private _findArrayDepth ( value , depth = 0 ) { if ( value && Array . isArray ( value ) ) { for ( var i = 0 , l = value . length ; i < l ; i ++ ) { depth = Math . max ( depth , this . _findArrayDepth ( value [ i ] , depth + 1 ) ) ; } } return depth ; } param value protected isQueryExpression ( value ) { if ( typeof value === "object" ) { for ( var key in value ) { if ( value . hasOwnProperty ( key ) ) { return key [ 0 ] == "$" ; } } } return false ; } }