Description
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;