@@ -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,25 @@ 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+
1014+ if ( options && options [ 'model' ] && options [ 'model' ] [ 'definition' ] ) {
1015+ const { relations} = options [ 'model' ] [ 'definition' ] ;
1016+ if ( relations ) {
1017+ const relationKeys = Object . keys ( relations ) ;
1018+ for ( let relationIndex = 0 ; relationIndex < relationKeys . length ; relationIndex ++ ) {
1019+ const relationName = relationKeys [ relationIndex ] ;
1020+ const relation = relations [ relationName ] ;
1021+ relationType = relation . type ;
1022+ relationKeyFrom = relation . keyFrom ;
1023+ if ( relationType === 'referencesMany' ) break ;
1024+ }
1025+ }
1026+ }
1027+ const whereClause = this . _buildWhere ( model , where , relationType , relationKeyFrom ) ;
1028+
10121029 if ( whereClause . sql ) {
10131030 whereClause . sql = 'WHERE ' + whereClause . sql ;
10141031 }
@@ -1024,7 +1041,8 @@ SQLConnector.prototype.buildWhere = function(model, where) {
10241041 * @returns {ParameterizedSQL } The SQL expression
10251042 */
10261043SQLConnector . prototype . buildExpression =
1027- function ( columnName , operator , columnValue , propertyValue ) {
1044+ function ( relationDetails , columnName , operator , columnValue , propertyValue ) {
1045+ const { relationType, relationKeyFrom} = relationDetails ;
10281046 function buildClause ( columnValue , separator , grouping ) {
10291047 const values = [ ] ;
10301048 for ( let i = 0 , n = columnValue . length ; i < n ; i ++ ) {
@@ -1067,8 +1085,24 @@ function(columnName, operator, columnValue, propertyValue) {
10671085 clause = buildClause ( columnValue , ' AND ' , false ) ;
10681086 break ;
10691087 case 'inq' :
1070- sqlExp += ' IN ' ;
1071- clause = buildClause ( columnValue , ',' , true ) ;
1088+ if ( relationType === 'referencesMany' && `\`${ relationKeyFrom } \`` === columnName ) {
1089+ sqlExp = '' ;
1090+ if ( columnValue . length === 1 ) {
1091+ sqlExp = `JSON_CONTAINS(${ columnName } , CAST(${ columnValue [ 0 ] } as JSON))` ;
1092+ } else {
1093+ columnValue . forEach ( value => {
1094+ sqlExp += `JSON_CONTAINS(${ columnName } , CAST(${ value } as JSON)) OR ` ;
1095+ } ) ;
1096+ const trimmed = sqlExp . trimEnd ( ) ;
1097+ if ( trimmed . endsWith ( 'OR' ) ) {
1098+ sqlExp = trimmed . slice ( 0 , trimmed . lastIndexOf ( 'OR' ) ) ;
1099+ }
1100+ }
1101+ clause = null ;
1102+ } else {
1103+ sqlExp += ' IN ' ;
1104+ clause = buildClause ( columnValue , ',' , true ) ;
1105+ }
10721106 break ;
10731107 case 'nin' :
10741108 sqlExp += ' NOT IN ' ;
@@ -1102,125 +1136,149 @@ function(columnName, operator, columnValue, propertyValue) {
11021136 * @param where
11031137 * @returns {ParameterizedSQL }
11041138 */
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 ) ;
1139+ SQLConnector . prototype . _buildWhere =
1140+ function ( model , where , relationType , relationKeyFrom ) {
1141+ let columnValue , sqlExp ;
1142+ if ( ! where ) {
1143+ return new ParameterizedSQL ( '' ) ;
1144+ }
1145+ if ( typeof where !== 'object' || Array . isArray ( where ) ) {
1146+ debug ( 'Invalid value for where: %j' , where ) ;
1147+ return new ParameterizedSQL ( '' ) ;
1148+ }
1149+ const self = this ;
1150+ const props = self . getModelDefinition ( model ) . properties ;
1151+
1152+ const whereStmts = [ ] ;
1153+ for ( const key in where ) {
1154+ const stmt = new ParameterizedSQL ( '' , [ ] ) ;
1155+ // Handle and/or operators
1156+ if ( key === 'and' || key === 'or' ) {
1157+ const branches = [ ] ;
1158+ let branchParams = [ ] ;
1159+ const clauses = where [ key ] ;
1160+ if ( Array . isArray ( clauses ) ) {
1161+ for ( let i = 0 , n = clauses . length ; i < n ; i ++ ) {
1162+ const stmtForClause = self
1163+ . _buildWhere ( model , clauses [ i ] , relationType , relationKeyFrom ) ;
1164+ if ( stmtForClause . sql ) {
1165+ stmtForClause . sql = '(' + stmtForClause . sql + ')' ;
1166+ branchParams = branchParams . concat ( stmtForClause . params ) ;
1167+ branches . push ( stmtForClause . sql ) ;
1168+ }
11321169 }
1170+ stmt . merge ( {
1171+ sql : '(' + branches . join ( ' ' + key . toUpperCase ( ) + ' ' ) + ')' ,
1172+ params : branchParams ,
1173+ } ) ;
1174+ whereStmts . push ( stmt ) ;
1175+ continue ;
11331176 }
1134- stmt . merge ( {
1135- sql : '(' + branches . join ( ' ' + key . toUpperCase ( ) + ' ' ) + ')' ,
1136- params : branchParams ,
1137- } ) ;
1138- whereStmts . push ( stmt ) ;
1177+ // The value is not an array, fall back to regular fields
1178+ }
1179+ const p = props [ key ] ;
1180+ if ( p == null ) {
1181+ // Unknown property, ignore it
1182+ debug ( 'Unknown property %s is skipped for model %s' , key , model ) ;
11391183 continue ;
11401184 }
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 ] ) ) ;
1185+ // eslint-disable one-var
1186+ let expression = where [ key ] ;
1187+ const columnName = self . columnEscaped ( model , key ) ;
1188+ // eslint-enable one-var
1189+ if ( expression === null || expression === undefined ) {
1190+ stmt . merge ( columnName + ' IS NULL' ) ;
1191+ } else if ( expression && expression . constructor === Object ) {
1192+ const operator = Object . keys ( expression ) [ 0 ] ;
1193+ // Get the expression without the operator
1194+ expression = expression [ operator ] ;
1195+ if ( operator === 'inq' || operator === 'nin' || operator === 'between' ) {
1196+ columnValue = [ ] ;
1197+ if ( Array . isArray ( expression ) ) {
1198+ // Column value is a list
1199+ for ( let j = 0 , m = expression . length ; j < m ; j ++ ) {
1200+ columnValue . push ( this . toColumnValue ( p , expression [ j ] ) ) ;
1201+ }
1202+ } else {
1203+ columnValue . push ( this . toColumnValue ( p , expression ) ) ;
11651204 }
1205+ if ( operator === 'between' ) {
1206+ // BETWEEN v1 AND v2
1207+ const v1 = columnValue [ 0 ] === undefined ? null : columnValue [ 0 ] ;
1208+ const v2 = columnValue [ 1 ] === undefined ? null : columnValue [ 1 ] ;
1209+ columnValue = [ v1 , v2 ] ;
1210+ } else {
1211+ // IN (v1,v2,v3) or NOT IN (v1,v2,v3)
1212+ if ( columnValue . length === 0 ) {
1213+ if ( operator === 'inq' ) {
1214+ columnValue = [ null ] ;
1215+ } else {
1216+ // nin () is true
1217+ continue ;
1218+ }
1219+ }
1220+ }
1221+ } else if ( operator === 'regexp' && expression instanceof RegExp ) {
1222+ // do not coerce RegExp based on property definitions
1223+ columnValue = expression ;
11661224 } else {
1167- columnValue . push ( this . toColumnValue ( p , expression ) ) ;
1225+ columnValue = this . toColumnValue ( p , expression ) ;
11681226 }
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 ] ;
1227+ sqlExp = self
1228+ . buildExpression (
1229+ { relationType, relationKeyFrom} ,
1230+ columnName , operator , columnValue ,
1231+ p ,
1232+ ) ;
1233+ if (
1234+ relationType === 'referencesMany' &&
1235+ `\`${ relationKeyFrom } \`` === columnName
1236+ ) {
1237+ stmt . merge ( sqlExp , columnValue ) ;
11741238 } 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- }
1239+ stmt . merge ( sqlExp ) ;
11841240 }
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' ) ;
11981241 } else {
1199- if ( columnValue instanceof ParameterizedSQL ) {
1200- stmt . merge ( columnName + '=' ) . merge ( columnValue ) ;
1242+ // The expression is the field value, not a condition
1243+ columnValue = self . toColumnValue ( p , expression ) ;
1244+ if ( columnValue === null ) {
1245+ stmt . merge ( columnName + ' IS NULL' ) ;
12011246 } else {
1202- stmt . merge ( {
1203- sql : columnName + '=?' ,
1204- params : [ columnValue ] ,
1205- } ) ;
1247+ if ( columnValue instanceof ParameterizedSQL ) {
1248+ stmt . merge ( columnName + '=' ) . merge ( columnValue ) ;
1249+ } else {
1250+ if (
1251+ relationType === 'referencesMany' &&
1252+ `\`${ relationKeyFrom } \`` === columnName
1253+ ) {
1254+ stmt . merge ( {
1255+ sql : `JSON_CONTAINS(${ columnName } , CAST(? as JSON))` ,
1256+ params : [ columnValue ] ,
1257+ } ) ;
1258+ } else {
1259+ stmt . merge ( {
1260+ sql : columnName + '=?' ,
1261+ params : [ columnValue ] ,
1262+ } ) ;
1263+ }
1264+ }
12061265 }
12071266 }
1267+ whereStmts . push ( stmt ) ;
12081268 }
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- } ;
1269+ let params = [ ] ;
1270+ const sqls = [ ] ;
1271+ for ( let k = 0 , s = whereStmts . length ; k < s ; k ++ ) {
1272+ if ( ! whereStmts [ k ] . sql ) continue ;
1273+ sqls . push ( whereStmts [ k ] . sql ) ;
1274+ params = params . concat ( whereStmts [ k ] . params ) ;
1275+ }
1276+ const whereStmt = new ParameterizedSQL ( {
1277+ sql : sqls . join ( ' AND ' ) ,
1278+ params : params ,
1279+ } ) ;
1280+ return whereStmt ;
1281+ } ;
12241282
12251283/**
12261284 * Build the ORDER BY clause
@@ -1453,7 +1511,7 @@ SQLConnector.prototype.buildSelect = function(model, filter, options) {
14531511
14541512 if ( filter ) {
14551513 if ( filter . where ) {
1456- const whereStmt = this . buildWhere ( model , filter . where ) ;
1514+ const whereStmt = this . buildWhere ( model , filter . where , options ) ;
14571515 selectStmt . merge ( whereStmt ) ;
14581516 }
14591517
@@ -1592,7 +1650,7 @@ SQLConnector.prototype.count = function(model, where, options, cb) {
15921650
15931651 let stmt = new ParameterizedSQL ( 'SELECT count(*) as "cnt" FROM ' +
15941652 this . tableEscaped ( model ) ) ;
1595- stmt = stmt . merge ( this . buildWhere ( model , where ) ) ;
1653+ stmt = stmt . merge ( this . buildWhere ( model , where , options ) ) ;
15961654 stmt = this . parameterize ( stmt ) ;
15971655 this . execute ( stmt . sql , stmt . params , options ,
15981656 function ( err , res ) {
0 commit comments