@@ -912,7 +912,7 @@ SQLConnector.prototype._buildWhereObjById = function(model, id, data) {
912912 */
913913SQLConnector . prototype . buildUpdate = function ( model , where , data , options ) {
914914 const fields = this . buildFieldsForUpdate ( model , data ) ;
915- return this . _constructUpdateQuery ( model , where , fields ) ;
915+ return this . _constructUpdateQuery ( model , where , fields , options ) ;
916916} ;
917917
918918/**
@@ -936,9 +936,9 @@ SQLConnector.prototype.buildReplace = function(model, where, data, options) {
936936 * @returns {Object } update query Constructed update query.
937937 * @private
938938 */
939- SQLConnector . prototype . _constructUpdateQuery = function ( model , where , fields ) {
939+ SQLConnector . prototype . _constructUpdateQuery = function ( model , where , fields , options ) {
940940 const updateClause = new ParameterizedSQL ( 'UPDATE ' + this . tableEscaped ( model ) ) ;
941- const whereClause = this . buildWhere ( model , where ) ;
941+ const whereClause = this . buildWhere ( model , where , options ) ;
942942 updateClause . merge ( [ fields , whereClause ] ) ;
943943 return this . parameterize ( updateClause ) ;
944944} ;
@@ -1007,8 +1007,23 @@ Connector.defineAliases(SQLConnector.prototype, 'replace', ['replaceAll']);
10071007 * @param {object } where An object for the where conditions
10081008 * @returns {ParameterizedSQL } The SQL WHERE clause
10091009 */
1010- SQLConnector . prototype . buildWhere = function ( model , where ) {
1011- const whereClause = this . _buildWhere ( model , where ) ;
1010+ SQLConnector . prototype . buildWhere = function ( model , where , options ) {
1011+ let relationType = '' ;
1012+ let relationKeyFrom = '' ;
1013+ if ( options && options [ 'model' ] && options [ 'model' ] [ 'definition' ] ) {
1014+ const { relations} = options [ 'model' ] [ 'definition' ] ;
1015+ if ( relations ) {
1016+ const relationKeys = Object . keys ( relations ) ;
1017+ for ( let relationIndex = 0 ; relationIndex < relationKeys . length ; relationIndex ++ ) {
1018+ const relationName = relationKeys [ relationIndex ] ;
1019+ const relation = relations [ relationName ] ;
1020+ relationType = relation . type ;
1021+ relationKeyFrom = relation . keyFrom ;
1022+ if ( relationType === 'referencesMany' ) break ;
1023+ }
1024+ }
1025+ }
1026+ const whereClause = this . _buildWhere ( model , where , relationType , relationKeyFrom ) ;
10121027 if ( whereClause . sql ) {
10131028 whereClause . sql = 'WHERE ' + whereClause . sql ;
10141029 }
@@ -1024,7 +1039,8 @@ SQLConnector.prototype.buildWhere = function(model, where) {
10241039 * @returns {ParameterizedSQL } The SQL expression
10251040 */
10261041SQLConnector . prototype . buildExpression =
1027- function ( columnName , operator , columnValue , propertyValue ) {
1042+ function ( relationDetails , columnName , operator , columnValue , propertyValue ) {
1043+ const { relationType, relationKeyFrom} = relationDetails ;
10281044 function buildClause ( columnValue , separator , grouping ) {
10291045 const values = [ ] ;
10301046 for ( let i = 0 , n = columnValue . length ; i < n ; i ++ ) {
@@ -1067,8 +1083,21 @@ function(columnName, operator, columnValue, propertyValue) {
10671083 clause = buildClause ( columnValue , ' AND ' , false ) ;
10681084 break ;
10691085 case 'inq' :
1070- sqlExp += ' IN ' ;
1071- clause = buildClause ( columnValue , ',' , true ) ;
1086+ if ( relationType === 'referencesMany' && `\`${ relationKeyFrom } \`` === columnName ) {
1087+ sqlExp = '' ;
1088+ if ( columnValue . length === 1 ) {
1089+ sqlExp = `JSON_CONTAINS(${ columnName } , CAST(${ columnValue [ 0 ] } as JSON))` ;
1090+ } else {
1091+ columnValue . forEach ( value => {
1092+ sqlExp += `JSON_CONTAINS(${ columnName } , CAST(${ value } as JSON)) OR ` ;
1093+ } ) ;
1094+ sqlExp = sqlExp . replace ( / \s + O R \s * $ / , '' ) ;
1095+ }
1096+ clause = null ;
1097+ } else {
1098+ sqlExp += ' IN ' ;
1099+ clause = buildClause ( columnValue , ',' , true ) ;
1100+ }
10721101 break ;
10731102 case 'nin' :
10741103 sqlExp += ' NOT IN ' ;
@@ -1102,125 +1131,150 @@ function(columnName, operator, columnValue, propertyValue) {
11021131 * @param where
11031132 * @returns {ParameterizedSQL }
11041133 */
1105- SQLConnector . prototype . _buildWhere = function ( model , where ) {
1106- let columnValue , sqlExp ;
1107- if ( ! where ) {
1108- return new ParameterizedSQL ( '' ) ;
1109- }
1110- if ( typeof where !== 'object' || Array . isArray ( where ) ) {
1111- debug ( 'Invalid value for where: %j' , where ) ;
1112- return new ParameterizedSQL ( '' ) ;
1113- }
1114- const self = this ;
1115- const props = self . getModelDefinition ( model ) . properties ;
1116-
1117- const whereStmts = [ ] ;
1118- for ( const key in where ) {
1119- const stmt = new ParameterizedSQL ( '' , [ ] ) ;
1120- // Handle and/or operators
1121- if ( key === 'and' || key === 'or' ) {
1122- const branches = [ ] ;
1123- let branchParams = [ ] ;
1124- const clauses = where [ key ] ;
1125- if ( Array . isArray ( clauses ) ) {
1126- for ( let i = 0 , n = clauses . length ; i < n ; i ++ ) {
1127- const stmtForClause = self . _buildWhere ( model , clauses [ i ] ) ;
1128- if ( stmtForClause . sql ) {
1129- stmtForClause . sql = '(' + stmtForClause . sql + ')' ;
1130- branchParams = branchParams . concat ( stmtForClause . params ) ;
1131- branches . push ( stmtForClause . sql ) ;
1134+ SQLConnector . prototype . _buildWhere =
1135+ function ( model , where , relationType , relationKeyFrom ) {
1136+ let columnValue , sqlExp ;
1137+ if ( ! where ) {
1138+ return new ParameterizedSQL ( '' ) ;
1139+ }
1140+ if ( typeof where !== 'object' || Array . isArray ( where ) ) {
1141+ debug ( 'Invalid value for where: %j' , where ) ;
1142+ return new ParameterizedSQL ( '' ) ;
1143+ }
1144+ const self = this ;
1145+ const props = self . getModelDefinition ( model ) . properties ;
1146+
1147+ const whereStmts = [ ] ;
1148+ for ( const key in where ) {
1149+ const stmt = new ParameterizedSQL ( '' , [ ] ) ;
1150+ // Handle and/or operators
1151+ if ( key === 'and' || key === 'or' ) {
1152+ const branches = [ ] ;
1153+ let branchParams = [ ] ;
1154+ const clauses = where [ key ] ;
1155+ if ( Array . isArray ( clauses ) ) {
1156+ for ( let i = 0 , n = clauses . length ; i < n ; i ++ ) {
1157+ const stmtForClause = self
1158+ . _buildWhere ( model , clauses [ i ] , relationType , relationKeyFrom ) ;
1159+ if ( stmtForClause . sql ) {
1160+ stmtForClause . sql = '(' + stmtForClause . sql + ')' ;
1161+ branchParams = branchParams . concat ( stmtForClause . params ) ;
1162+ branches . push ( stmtForClause . sql ) ;
1163+ }
11321164 }
1165+ stmt . merge ( {
1166+ sql : '(' + branches . join ( ' ' + key . toUpperCase ( ) + ' ' ) + ')' ,
1167+ params : branchParams ,
1168+ } ) ;
1169+ whereStmts . push ( stmt ) ;
1170+ continue ;
11331171 }
1134- stmt . merge ( {
1135- sql : '(' + branches . join ( ' ' + key . toUpperCase ( ) + ' ' ) + ')' ,
1136- params : branchParams ,
1137- } ) ;
1138- whereStmts . push ( stmt ) ;
1172+ // The value is not an array, fall back to regular fields
1173+ }
1174+ const p = props [ key ] ;
1175+ if ( p == null ) {
1176+ // Unknown property, ignore it
1177+ debug ( 'Unknown property %s is skipped for model %s' , key , model ) ;
11391178 continue ;
11401179 }
1141- // The value is not an array, fall back to regular fields
1142- }
1143- const p = props [ key ] ;
1144- if ( p == null ) {
1145- // Unknown property, ignore it
1146- debug ( 'Unknown property %s is skipped for model %s' , key , model ) ;
1147- continue ;
1148- }
1149- // eslint-disable one-var
1150- let expression = where [ key ] ;
1151- const columnName = self . columnEscaped ( model , key ) ;
1152- // eslint-enable one-var
1153- if ( expression === null || expression === undefined ) {
1154- stmt . merge ( columnName + ' IS NULL' ) ;
1155- } else if ( expression && expression . constructor === Object ) {
1156- const operator = Object . keys ( expression ) [ 0 ] ;
1157- // Get the expression without the operator
1158- expression = expression [ operator ] ;
1159- if ( operator === 'inq' || operator === 'nin' || operator === 'between' ) {
1160- columnValue = [ ] ;
1161- if ( Array . isArray ( expression ) ) {
1162- // Column value is a list
1163- for ( let j = 0 , m = expression . length ; j < m ; j ++ ) {
1164- columnValue . push ( this . toColumnValue ( p , expression [ j ] ) ) ;
1180+ // eslint-disable one-var
1181+ let expression = where [ key ] ;
1182+ const columnName = self . columnEscaped ( model , key ) ;
1183+ // eslint-enable one-var
1184+ if ( expression === null || expression === undefined ) {
1185+ stmt . merge ( columnName + ' IS NULL' ) ;
1186+ } else if ( expression && expression . constructor === Object ) {
1187+ const operator = Object . keys ( expression ) [ 0 ] ;
1188+ // Get the expression without the operator
1189+ expression = expression [ operator ] ;
1190+ if ( operator === 'inq' || operator === 'nin' || operator === 'between' ) {
1191+ columnValue = [ ] ;
1192+ if ( Array . isArray ( expression ) ) {
1193+ // Column value is a list
1194+ for ( let j = 0 , m = expression . length ; j < m ; j ++ ) {
1195+ columnValue . push ( this . toColumnValue ( p , expression [ j ] ) ) ;
1196+ }
1197+ } else {
1198+ columnValue . push ( this . toColumnValue ( p , expression ) ) ;
11651199 }
1200+ if ( operator === 'between' ) {
1201+ // BETWEEN v1 AND v2
1202+ const v1 = columnValue [ 0 ] === undefined ? null : columnValue [ 0 ] ;
1203+ const v2 = columnValue [ 1 ] === undefined ? null : columnValue [ 1 ] ;
1204+ columnValue = [ v1 , v2 ] ;
1205+ } else {
1206+ // IN (v1,v2,v3) or NOT IN (v1,v2,v3)
1207+ if ( columnValue . length === 0 ) {
1208+ if ( operator === 'inq' ) {
1209+ columnValue = [ null ] ;
1210+ } else {
1211+ // nin () is true
1212+ continue ;
1213+ }
1214+ }
1215+ }
1216+ } else if ( operator === 'regexp' && expression instanceof RegExp ) {
1217+ // do not coerce RegExp based on property definitions
1218+ columnValue = expression ;
11661219 } else {
1167- columnValue . push ( this . toColumnValue ( p , expression ) ) ;
1220+ columnValue = this . toColumnValue ( p , expression ) ;
11681221 }
1169- if ( operator === 'between' ) {
1170- // BETWEEN v1 AND v2
1171- const v1 = columnValue [ 0 ] === undefined ? null : columnValue [ 0 ] ;
1172- const v2 = columnValue [ 1 ] === undefined ? null : columnValue [ 1 ] ;
1173- columnValue = [ v1 , v2 ] ;
1222+ if ( `\`${ relationKeyFrom } \`` !== columnName ) { relationType = '' ; }
1223+ sqlExp = self
1224+ . buildExpression (
1225+ { relationType, relationKeyFrom} ,
1226+ columnName , operator , columnValue ,
1227+ p ,
1228+ ) ;
1229+ if (
1230+ relationType === 'referencesMany' &&
1231+ `\`${ relationKeyFrom } \`` === columnName
1232+ ) {
1233+ stmt . merge ( sqlExp , columnValue ) ;
11741234 } else {
1175- // IN (v1,v2,v3) or NOT IN (v1,v2,v3)
1176- if ( columnValue . length === 0 ) {
1177- if ( operator === 'inq' ) {
1178- columnValue = [ null ] ;
1179- } else {
1180- // nin () is true
1181- continue ;
1182- }
1183- }
1235+ stmt . merge ( sqlExp ) ;
11841236 }
1185- } else if ( operator === 'regexp' && expression instanceof RegExp ) {
1186- // do not coerce RegExp based on property definitions
1187- columnValue = expression ;
1188- } else {
1189- columnValue = this . toColumnValue ( p , expression ) ;
1190- }
1191- sqlExp = self . buildExpression ( columnName , operator , columnValue , p ) ;
1192- stmt . merge ( sqlExp ) ;
1193- } else {
1194- // The expression is the field value, not a condition
1195- columnValue = self . toColumnValue ( p , expression ) ;
1196- if ( columnValue === null ) {
1197- stmt . merge ( columnName + ' IS NULL' ) ;
11981237 } else {
1199- if ( columnValue instanceof ParameterizedSQL ) {
1200- stmt . merge ( columnName + '=' ) . merge ( columnValue ) ;
1238+ // The expression is the field value, not a condition
1239+ columnValue = self . toColumnValue ( p , expression ) ;
1240+ if ( columnValue === null ) {
1241+ stmt . merge ( columnName + ' IS NULL' ) ;
12011242 } else {
1202- stmt . merge ( {
1203- sql : columnName + '=?' ,
1204- params : [ columnValue ] ,
1205- } ) ;
1243+ if ( columnValue instanceof ParameterizedSQL ) {
1244+ stmt . merge ( columnName + '=' ) . merge ( columnValue ) ;
1245+ } else {
1246+ if (
1247+ relationType === 'referencesMany' &&
1248+ `\`${ relationKeyFrom } \`` === columnName
1249+ ) {
1250+ stmt . merge ( {
1251+ sql : `JSON_CONTAINS(${ columnName } , CAST(? as JSON))` ,
1252+ params : [ columnValue ] ,
1253+ } ) ;
1254+ } else {
1255+ stmt . merge ( {
1256+ sql : columnName + '=?' ,
1257+ params : [ columnValue ] ,
1258+ } ) ;
1259+ }
1260+ }
12061261 }
12071262 }
1263+ whereStmts . push ( stmt ) ;
12081264 }
1209- whereStmts . push ( stmt ) ;
1210- }
1211- let params = [ ] ;
1212- const sqls = [ ] ;
1213- for ( let k = 0 , s = whereStmts . length ; k < s ; k ++ ) {
1214- if ( ! whereStmts [ k ] . sql ) continue ;
1215- sqls . push ( whereStmts [ k ] . sql ) ;
1216- params = params . concat ( whereStmts [ k ] . params ) ;
1217- }
1218- const whereStmt = new ParameterizedSQL ( {
1219- sql : sqls . join ( ' AND ' ) ,
1220- params : params ,
1221- } ) ;
1222- return whereStmt ;
1223- } ;
1265+ let params = [ ] ;
1266+ const sqls = [ ] ;
1267+ for ( let k = 0 , s = whereStmts . length ; k < s ; k ++ ) {
1268+ if ( ! whereStmts [ k ] . sql ) continue ;
1269+ sqls . push ( whereStmts [ k ] . sql ) ;
1270+ params = params . concat ( whereStmts [ k ] . params ) ;
1271+ }
1272+ const whereStmt = new ParameterizedSQL ( {
1273+ sql : sqls . join ( ' AND ' ) ,
1274+ params : params ,
1275+ } ) ;
1276+ return whereStmt ;
1277+ } ;
12241278
12251279/**
12261280 * Build the ORDER BY clause
@@ -1453,7 +1507,7 @@ SQLConnector.prototype.buildSelect = function(model, filter, options) {
14531507
14541508 if ( filter ) {
14551509 if ( filter . where ) {
1456- const whereStmt = this . buildWhere ( model , filter . where ) ;
1510+ const whereStmt = this . buildWhere ( model , filter . where , options ) ;
14571511 selectStmt . merge ( whereStmt ) ;
14581512 }
14591513
@@ -1592,7 +1646,7 @@ SQLConnector.prototype.count = function(model, where, options, cb) {
15921646
15931647 let stmt = new ParameterizedSQL ( 'SELECT count(*) as "cnt" FROM ' +
15941648 this . tableEscaped ( model ) ) ;
1595- stmt = stmt . merge ( this . buildWhere ( model , where ) ) ;
1649+ stmt = stmt . merge ( this . buildWhere ( model , where , options ) ) ;
15961650 stmt = this . parameterize ( stmt ) ;
15971651 this . execute ( stmt . sql , stmt . params , options ,
15981652 function ( err , res ) {
0 commit comments