|
1 | 1 | using Magic.IndexedDb.Helpers; |
2 | 2 | using Magic.IndexedDb.Models.UniversalOperations; |
3 | 3 | using System; |
| 4 | +using System.Collections; |
4 | 5 | using System.Collections.Generic; |
5 | 6 | using System.Linq; |
6 | 7 | using System.Linq.Expressions; |
@@ -137,6 +138,18 @@ void AddBinary(BinaryExpression bin, bool isInOr) |
137 | 138 |
|
138 | 139 | void AddMethodCall(MethodCallExpression call, bool isInOr) |
139 | 140 | { |
| 141 | + // Flatten value-array.Contains(x.Property) or x.Collection.Contains(value) |
| 142 | + if (TryFlattenContains(call, isInOr, out var flattened)) |
| 143 | + { |
| 144 | + foreach (var cond in flattened) |
| 145 | + { |
| 146 | + var individualGroup = new AndFilterGroup(); |
| 147 | + individualGroup.conditions.Add(cond); |
| 148 | + orGroup.andGroups.Add(individualGroup); // Treat them as parallel OR options |
| 149 | + } |
| 150 | + return; |
| 151 | + } |
| 152 | + |
140 | 153 | string name = call.Method.Name; |
141 | 154 | string resolvedOp = name switch |
142 | 155 | { |
@@ -207,6 +220,83 @@ void AddConditionInternal(MemberExpression? left, ConstantExpression? right, str |
207 | 220 | return orGroup; |
208 | 221 | } |
209 | 222 |
|
| 223 | + |
| 224 | + private bool TryFlattenContains(MethodCallExpression call, bool isOr, out IEnumerable<FilterCondition> flattened) |
| 225 | + { |
| 226 | + flattened = Enumerable.Empty<FilterCondition>(); |
| 227 | + |
| 228 | + if (call.Method.Name != "Contains") |
| 229 | + return false; |
| 230 | + |
| 231 | + // Case 1: Static-style — myArray.Contains(x._Age) |
| 232 | + if (call.Object == null && call.Arguments.Count == 2) |
| 233 | + { |
| 234 | + var maybeCollection = call.Arguments[0]; |
| 235 | + var maybeProp = call.Arguments[1]; |
| 236 | + |
| 237 | + if ((maybeCollection is MemberExpression || maybeCollection is ConstantExpression) && |
| 238 | + maybeProp is MemberExpression propExpr && |
| 239 | + IsParamMember(propExpr)) |
| 240 | + { |
| 241 | + var collectionExpr = maybeCollection; |
| 242 | + var collection = Expression.Lambda(collectionExpr).Compile().DynamicInvoke(); |
| 243 | + |
| 244 | + if (collection is IEnumerable enumerable) |
| 245 | + { |
| 246 | + var propInfo = typeof(T).GetProperty(propExpr.Member.Name); |
| 247 | + if (propInfo == null) |
| 248 | + return false; |
| 249 | + |
| 250 | + string jsProp = PropertyMappingCache.GetJsPropertyName<T>(propInfo); |
| 251 | + |
| 252 | + flattened = enumerable |
| 253 | + .Cast<object?>() |
| 254 | + .Select(val => |
| 255 | + new FilterCondition( |
| 256 | + jsProp, |
| 257 | + "Equal", |
| 258 | + val != null ? JsonValue.Create(val) : null, |
| 259 | + val is string, |
| 260 | + false |
| 261 | + ) |
| 262 | + ); |
| 263 | + |
| 264 | + return true; |
| 265 | + } |
| 266 | + } |
| 267 | + } |
| 268 | + |
| 269 | + // Case 2: Instance-style — x.CollectionProperty.Contains(10) |
| 270 | + if (call.Object is MemberExpression collectionMember && |
| 271 | + call.Arguments.Count == 1 && |
| 272 | + call.Arguments[0] is ConstantExpression constant) |
| 273 | + { |
| 274 | + var propInfo = typeof(T).GetProperty(collectionMember.Member.Name); |
| 275 | + if (propInfo == null) return false; |
| 276 | + |
| 277 | + string jsProp = PropertyMappingCache.GetJsPropertyName<T>(propInfo); |
| 278 | + |
| 279 | + flattened = new[] |
| 280 | + { |
| 281 | + new FilterCondition( |
| 282 | + jsProp, |
| 283 | + "ArrayContains", |
| 284 | + JsonValue.Create(constant.Value), |
| 285 | + constant.Value is string, |
| 286 | + false |
| 287 | + ) |
| 288 | + }; |
| 289 | + |
| 290 | + return true; |
| 291 | + } |
| 292 | + |
| 293 | + return false; |
| 294 | + } |
| 295 | + |
| 296 | + |
| 297 | + |
| 298 | + |
| 299 | + |
210 | 300 | private static bool SupportedMethodNameForNegation(string name) => |
211 | 301 | name is "Contains" or "StartsWith" or "EndsWith" or "Equals"; |
212 | 302 |
|
@@ -241,6 +331,21 @@ private static bool ExtractCaseSensitivity(MethodCallExpression call) |
241 | 331 | "LessThanOrEqual" => "GreaterThanOrEqual", |
242 | 332 | _ => op |
243 | 333 | }; |
| 334 | + |
| 335 | + private static ConstantExpression ToConst(Expression expr) => expr switch |
| 336 | + { |
| 337 | + ConstantExpression c => c, |
| 338 | + MemberExpression m => Expression.Constant(Expression.Lambda(m).Compile().DynamicInvoke()), |
| 339 | + _ => throw new InvalidOperationException($"Unsupported or non-constant: {expr}") |
| 340 | + }; |
| 341 | + |
| 342 | + private static bool IsParamMember(Expression expr) |
| 343 | + { |
| 344 | + return expr is MemberExpression member && |
| 345 | + member.Expression is ParameterExpression; |
| 346 | + } |
| 347 | + |
| 348 | + |
244 | 349 | } |
245 | 350 | // Your supporting model classes (OrFilterGroup, AndFilterGroup, FilterCondition, etc.) |
246 | 351 | // should stay as they are in their own files or namespaces. |
|
0 commit comments