Uploaded image for project: 'C# Driver'
  1. C# Driver
  2. CSHARP-4493

"An object representing an expression must have exactly one field" when querying by Linq query with .ToLower()

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Unknown Unknown
    • 2.19.2
    • Affects Version/s: 2.18.0
    • Component/s: LINQ3
    • Labels:
    • Fully Compatible
    • Not Needed
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?

      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;

       

            Assignee:
            oleksandr.poliakov@mongodb.com Oleksandr Poliakov
            Reporter:
            sorinpochtar@gmail.com Sorin Pochtar
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: