@@ -213,27 +213,51 @@ def construct_search_bindings(include, exclude, columns, exact=False, flexible_s
213213 sql = []
214214 bindings = {}
215215
216- incl = ":" + param_key + "include{0}"
217- includes = "(" + " OR " .join ([f"{ col } LIKE { incl } " for col in columns ]) + ")"
218- includes_sql_parts = []
219- for idx , inc in enumerate (include ):
220- includes_sql_parts .append (includes .format (idx ))
216+ def get_include_sql (idx_str , inc ):
217+ incl = ":" + param_key + f"include{ idx_str } "
221218 if exact :
222- bindings [f"{ param_key } include{ idx } " ] = inc
219+ bindings [f"{ param_key } include{ idx_str } " ] = inc
220+ else :
221+ bindings [f"{ param_key } include{ idx_str } " ] = "%" + inc .replace (" " , "%" ).replace ("%%" , " " ) + "%"
222+ return "(" + " OR " .join ([f"{ col } LIKE { incl } " for col in columns ]) + ")"
223+
224+ include_sql_parts = []
225+ for idx , inc in enumerate (include ):
226+ if isinstance (inc , list ):
227+ group_parts = [get_include_sql (f"{ idx } _{ sub_idx } " , sub_inc ) for sub_idx , sub_inc in enumerate (inc )]
228+ include_sql_parts .append ("(" + " OR " .join (group_parts ) + ")" )
223229 else :
224- bindings [f"{ param_key } include{ idx } " ] = "%" + inc .replace (" " , "%" ).replace ("%%" , " " ) + "%"
225- join_op = " OR " if flexible_search else " AND "
226- if len (includes_sql_parts ) > 0 :
227- sql .append ("AND (" + join_op .join (includes_sql_parts ) + ")" )
230+ include_sql_parts .append (get_include_sql (idx , inc ))
231+
232+ if flexible_search :
233+ if include_sql_parts :
234+ sql .append ("AND (" + " OR " .join (include_sql_parts ) + ")" )
235+ else :
236+ for part in include_sql_parts :
237+ sql .append ("AND " + part )
228238
229239 excl = ":" + param_key + "exclude{0}"
230240 excludes = "AND (" + " AND " .join ([f"COALESCE({ col } ,'') NOT LIKE { excl } " for col in columns ]) + ")"
231241 for idx , exc in enumerate (exclude ):
232- sql .append (excludes .format (idx ))
233- if exact :
234- bindings [f"{ param_key } exclude{ idx } " ] = exc
242+ if isinstance (exc , list ):
243+ # For exclusion, OR inside the group means if ANY variant matches, it's excluded
244+ # This is equivalent to AND NOT variant1 AND NOT variant2
245+ for sub_idx , sub_exc in enumerate (exc ):
246+ sub_idx_str = f"{ idx } _{ sub_idx } "
247+ bindings [f"{ param_key } exclude{ sub_idx_str } " ] = (
248+ sub_exc if exact else "%" + sub_exc .replace (" " , "%" ).replace ("%%" , " " ) + "%"
249+ )
250+ sql .append (
251+ "AND ("
252+ + " AND " .join ([f"COALESCE({ col } ,'') NOT LIKE :{ param_key } exclude{ sub_idx_str } " for col in columns ])
253+ + ")"
254+ )
235255 else :
236- bindings [f"{ param_key } exclude{ idx } " ] = "%" + exc .replace (" " , "%" ).replace ("%%" , " " ) + "%"
256+ sql .append (excludes .format (idx ))
257+ if exact :
258+ bindings [f"{ param_key } exclude{ idx } " ] = exc
259+ else :
260+ bindings [f"{ param_key } exclude{ idx } " ] = "%" + exc .replace (" " , "%" ).replace ("%%" , " " ) + "%"
237261
238262 return sql , bindings
239263
0 commit comments