@@ -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 } ${ columnValue [ 0 ] }  ; 
1090+         }  else  { 
1091+           columnValue . forEach ( value  =>  { 
1092+             sqlExp  +=  `JSON_CONTAINS(${ columnName } ${ value }  ; 
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 }  , 
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