Summary
"An object representing an expression must have exactly one field" exception occurs when querying by Linq query with multiple Any/Contains conditions, each of which having .ToLower() operator, and conditions combined by &&.
C# Driver version: 2.18.0
Server version: 6.0.2, standalone, hosted on Docker container
LinqProvider: LinqProvider.V3
How to Reproduce
Build the following Linq query and use Find(...) driver's method to query database:
Expression<Func<Customer, bool>> filterByAddressAndEmails = c => c.Emails.Any(ce => emails.Contains(ce.Email.ToLower())) && addresses.Contains(c.Address.FullAddress.ToLower());
where Customer class is:
public class Customer { [BsonId] [BsonIgnoreIfDefault] [BsonRepresentation(BsonType.String)] public ObjectId Id { get; set; } public AddressMetadata Address { get; set; } public IList<EmailMetadata> Emails { get; set; } ... public sealed record AddressMetadata { ... public string FullAddress { get; set; } ... } public sealed record EmailMetadata { ... public string Email { get; set; } .. } }
and variables are:
string[] emails = new[] { "email4@test.net" }; string[] addresses = new[] { "495 pacific street plymouth, ma 02360" };
the following exception is generated:
Command find failed: An object representing an expression must have exactly one field: { $in: [ { $toLower: "$Address.FullAddress" }, [ "495 pacific street plymouth, ma 02360" ] ], $anyElementTrue: { $map: { input: "$Emails", as: "ce", in: { $in: [ { $toLower: "$$ce.Email" }, [ "email4@test.net" ] ] } } } }
Additional Background
As a workaround to the issue, I had to split the query into two, one of which includes OR operator:
Expression<Func<Customer, bool>> filterByAddressAndEmails = c => c.Emails.Any(ce => emails.Contains(ce.Email.ToLower())); Expression<Func<Customer, bool>> filterByAddress = c => addresses.Contains(c.Address.FullAddress.ToLower()) || addresses.Contains(c.Address.FullAddress); filterByFullAddressAndEmails = filterByFullAddressAndEmails.And(filterByFullAddress); where .And(...) is an extension method which can combine Linq queries.
Also, if the original query is modified that one of .ToLower() methods are removed, the query does not generate an exception, but does not return a correct result, because I need case insensitive match;